diff options
Diffstat (limited to 'packages/linux/files')
19 files changed, 0 insertions, 74753 deletions
diff --git a/packages/linux/files/.mtn2git_empty b/packages/linux/files/.mtn2git_empty deleted file mode 100644 index e69de29bb2..0000000000 --- a/packages/linux/files/.mtn2git_empty +++ /dev/null diff --git a/packages/linux/files/ipaq-hal.init b/packages/linux/files/ipaq-hal.init deleted file mode 100644 index 4efb52ec97..0000000000 --- a/packages/linux/files/ipaq-hal.init +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/sh - -# make sure update-modules has been run -# since the calls below depend on aliases... -if [ ! -f /etc/modules.conf ]; then - update-modules || true -fi - -modprobe ipaq_hal || exit 0 - -if [ -d /proc/hal ]; then - model=`cat /proc/hal/model` - modprobe ipaq_hal_$model -fi diff --git a/packages/linux/files/ir240_sys_max_tx-2.diff b/packages/linux/files/ir240_sys_max_tx-2.diff deleted file mode 100644 index 5f1307d7dc..0000000000 --- a/packages/linux/files/ir240_sys_max_tx-2.diff +++ /dev/null @@ -1,110 +0,0 @@ ---- linux/net/irda/irsysctl.c.orig 2003-05-13 11:20:16.000000000 +0200 -+++ linux/net/irda/irsysctl.c 2005-01-22 18:39:40.496001712 +0100 -@@ -40,7 +40,8 @@ - - enum { DISCOVERY=1, DEVNAME, DEBUG, FAST_POLL, DISCOVERY_SLOTS, - DISCOVERY_TIMEOUT, SLOT_TIMEOUT, MAX_BAUD_RATE, MIN_TX_TURN_TIME, -- MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, LAP_KEEPALIVE_TIME, SPECIFIC_DEV }; -+ MAX_TX_DATA_SIZE, MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, LAP_KEEPALIVE_TIME, -+ SPECIFIC_DEV }; - - extern int sysctl_discovery; - extern int sysctl_discovery_slots; -@@ -51,6 +52,7 @@ - extern char sysctl_devname[]; - extern int sysctl_max_baud_rate; - extern int sysctl_min_tx_turn_time; -+extern int sysctl_max_tx_data_size; - extern int sysctl_max_noreply_time; - extern int sysctl_warn_noreply_time; - extern int sysctl_lap_keepalive_time; -@@ -71,6 +73,8 @@ - static int min_max_baud_rate = 2400; - static int max_min_tx_turn_time = 10000; /* See qos.c - IrLAP spec */ - static int min_min_tx_turn_time = 0; -+static int max_max_tx_data_size = 2048; /* See qos.c - IrLAP spec */ -+static int min_max_tx_data_size = 64; - static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */ - static int min_max_noreply_time = 3; - static int max_warn_noreply_time = 3; /* 3s == standard */ -@@ -128,6 +132,9 @@ - { MIN_TX_TURN_TIME, "min_tx_turn_time", &sysctl_min_tx_turn_time, - sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, - NULL, &min_min_tx_turn_time, &max_min_tx_turn_time }, -+ { MAX_TX_DATA_SIZE, "max_tx_data_size", &sysctl_max_tx_data_size, -+ sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, -+ NULL, &min_max_tx_data_size, &max_max_tx_data_size }, - { MAX_NOREPLY_TIME, "max_noreply_time", &sysctl_max_noreply_time, - sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, - NULL, &min_max_noreply_time, &max_max_noreply_time }, ---- linux/net/irda/qos.c.orig 2003-05-13 11:20:16.000000000 +0200 -+++ linux/net/irda/qos.c 2005-01-22 18:36:46.759413688 +0100 -@@ -60,10 +60,26 @@ - * Nonzero values (usec) are used as lower limit to the per-connection - * mtt value which was announced by the other end during negotiation. - * Might be helpful if the peer device provides too short mtt. -- * Default is 10 which means using the unmodified value given by the peer -- * except if it's 0 (0 is likely a bug in the other stack). -+ * Default is 10us which means using the unmodified value given by the -+ * peer except if it's 0 (0 is likely a bug in the other stack). - */ - unsigned sysctl_min_tx_turn_time = 10; -+/* -+ * Maximum data size to be used in transmission in payload of LAP frame. -+ * There is a bit of confusion in the IrDA spec : -+ * The LAP spec defines the payload of a LAP frame (I field) to be -+ * 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40). -+ * On the other hand, the PHY mention frames of 2048 bytes max (IrPHY -+ * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header -+ * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP -+ * payload), that's only 2042 bytes. Oups ! -+ * I've had trouble trouble transmitting 2048 bytes frames with USB -+ * dongles and nsc-ircc at 4 Mb/s, so adjust to 2042... I don't know -+ * if this bug applies only for 2048 bytes frames or all negociated -+ * frame sizes, but all hardware seem to support "2048 bytes" frames. -+ * You can use the sysctl to play with this value anyway. -+ * Jean II */ -+unsigned sysctl_max_tx_data_size = 2042; - - /* - * Specific device list limits some negotiation parameters at the connection -@@ -398,10 +414,10 @@ - while ((qos->data_size.value > line_capacity) && (index > 0)) { - qos->data_size.value = data_sizes[index--]; - IRDA_DEBUG(2, __FUNCTION__ -- "(), redusing data size to %d\n", -+ "(), reducing data size to %d\n", - qos->data_size.value); - } --#else /* Use method descibed in section 6.6.11 of IrLAP */ -+#else /* Use method described in section 6.6.11 of IrLAP */ - while (irlap_requested_line_capacity(qos) > line_capacity) { - ASSERT(index != 0, return;); - -@@ -409,18 +425,24 @@ - if (qos->window_size.value > 1) { - qos->window_size.value--; - IRDA_DEBUG(2, __FUNCTION__ -- "(), redusing window size to %d\n", -+ "(), reducing window size to %d\n", - qos->window_size.value); - } else if (index > 1) { - qos->data_size.value = data_sizes[index--]; - IRDA_DEBUG(2, __FUNCTION__ -- "(), redusing data size to %d\n", -+ "(), reducing data size to %d\n", - qos->data_size.value); - } else { - WARNING(__FUNCTION__ "(), nothing more we can do!\n"); - } - } - #endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ -+ /* -+ * Fix tx data size according to user limits - Jean II -+ */ -+ if (qos->data_size.value > sysctl_max_tx_data_size) -+ /* Allow non discrete adjustement to avoid loosing capacity */ -+ qos->data_size.value = sysctl_max_tx_data_size; - } - - /* diff --git a/packages/linux/files/ir241_qos_param-2.diff b/packages/linux/files/ir241_qos_param-2.diff deleted file mode 100644 index dfe77c52b0..0000000000 --- a/packages/linux/files/ir241_qos_param-2.diff +++ /dev/null @@ -1,164 +0,0 @@ ---- linux/net/irda/qos.c.orig 2005-01-22 19:19:56.013787192 +0100 -+++ linux/net/irda/qos.c 2005-01-22 19:21:13.493008560 +0100 -@@ -73,13 +73,18 @@ - * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header - * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP - * payload), that's only 2042 bytes. Oups ! -- * I've had trouble trouble transmitting 2048 bytes frames with USB -- * dongles and nsc-ircc at 4 Mb/s, so adjust to 2042... I don't know -- * if this bug applies only for 2048 bytes frames or all negociated -- * frame sizes, but all hardware seem to support "2048 bytes" frames. -- * You can use the sysctl to play with this value anyway. -+ * My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s, -+ * so adjust to 2042... I don't know if this bug applies only for 2048 -+ * bytes frames or all negociated frame sizes, but you can use the sysctl -+ * to play with this value anyway. - * Jean II */ - unsigned sysctl_max_tx_data_size = 2042; -+/* -+ * Maximum transmit window, i.e. number of LAP frames between turn-around. -+ * This allow to override what the peer told us. Some peers are buggy and -+ * don't always support what they tell us. -+ * Jean II */ -+unsigned sysctl_max_tx_window = 7; - - /* - * Specific device list limits some negotiation parameters at the connection -@@ -227,7 +232,19 @@ - { - __u16 msb = 0x8000; - int index = 15; /* Current MSB */ -- -+ -+ /* Check for buggy peers. -+ * Note : there is a small probability that it could be us, but I -+ * would expect driver authors to catch that pretty early and be -+ * able to check precisely what's going on. If a end user sees this, -+ * it's very likely the peer. - Jean II */ -+ if (word == 0) { -+ WARNING("%s(), Detected buggy peer, adjust null PV to 0x1!\n", -+ __FUNCTION__); -+ /* The only safe choice (we don't know the array size) */ -+ word = 0x1; -+ } -+ - while (msb) { - if (word & msb) - break; /* Found it! */ -@@ -378,10 +395,14 @@ - - /* - * Make sure the mintt is sensible. -+ * Main culprit : Ericsson T39. - Jean II - */ - if (sysctl_min_tx_turn_time > qos->min_turn_time.value) { - int i; - -+ WARNING("%s(), Detected buggy peer, adjust mtt to %dus!\n", -+ __FUNCTION__, sysctl_min_tx_turn_time); -+ - /* We don't really need bits, but easier this way */ - i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times, - 8, &qos->min_turn_time.bits); -@@ -443,6 +464,11 @@ - if (qos->data_size.value > sysctl_max_tx_data_size) - /* Allow non discrete adjustement to avoid loosing capacity */ - qos->data_size.value = sysctl_max_tx_data_size; -+ /* -+ * Override Tx window if user request it. - Jean II -+ */ -+ if (qos->window_size.value > sysctl_max_tx_window) -+ qos->window_size.value = sysctl_max_tx_window; - } - - /* ---- linux/net/irda/irsysctl.c.orig 2005-01-22 19:19:56.006788256 +0100 -+++ linux/net/irda/irsysctl.c 2005-01-22 19:24:31.273941288 +0100 -@@ -40,8 +40,8 @@ - - enum { DISCOVERY=1, DEVNAME, DEBUG, FAST_POLL, DISCOVERY_SLOTS, - DISCOVERY_TIMEOUT, SLOT_TIMEOUT, MAX_BAUD_RATE, MIN_TX_TURN_TIME, -- MAX_TX_DATA_SIZE, MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, LAP_KEEPALIVE_TIME, -- SPECIFIC_DEV }; -+ MAX_TX_DATA_SIZE, MAX_TX_WINDOW, MAX_NOREPLY_TIME, WARN_NOREPLY_TIME, -+ LAP_KEEPALIVE_TIME, SPECIFIC_DEV }; - - extern int sysctl_discovery; - extern int sysctl_discovery_slots; -@@ -53,6 +53,7 @@ - extern int sysctl_max_baud_rate; - extern int sysctl_min_tx_turn_time; - extern int sysctl_max_tx_data_size; -+extern int sysctl_max_tx_window; - extern int sysctl_max_noreply_time; - extern int sysctl_warn_noreply_time; - extern int sysctl_lap_keepalive_time; -@@ -75,6 +76,8 @@ - static int min_min_tx_turn_time = 0; - static int max_max_tx_data_size = 2048; /* See qos.c - IrLAP spec */ - static int min_max_tx_data_size = 64; -+static int max_max_tx_window = 7; /* See qos.c - IrLAP spec */ -+static int min_max_tx_window = 1; - static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */ - static int min_max_noreply_time = 3; - static int max_warn_noreply_time = 3; /* 3s == standard */ -@@ -135,6 +138,9 @@ - { MAX_TX_DATA_SIZE, "max_tx_data_size", &sysctl_max_tx_data_size, - sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, - NULL, &min_max_tx_data_size, &max_max_tx_data_size }, -+ { MAX_TX_WINDOW, "max_tx_window", &sysctl_max_tx_window, -+ sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, -+ NULL, &min_max_tx_window, &max_max_tx_window }, - { MAX_NOREPLY_TIME, "max_noreply_time", &sysctl_max_noreply_time, - sizeof(int), 0644, NULL, &proc_dointvec_minmax, &sysctl_intvec, - NULL, &min_max_noreply_time, &max_max_noreply_time }, ---- linux/net/irda/parameters.c.orig 2003-05-13 11:20:16.000000000 +0200 -+++ linux/net/irda/parameters.c 2005-01-22 19:21:13.527003392 +0100 -@@ -204,11 +204,13 @@ - { - irda_param_t p; - int n = 0; -+ int extract_len; /* Real lenght we extract */ - int err; - - p.pi = pi; /* In case handler needs to know */ - p.pl = buf[1]; /* Extract lenght of value */ - p.pv.i = 0; /* Clear value */ -+ extract_len = p.pl; /* Default : extract all */ - - /* Check if buffer is long enough for parsing */ - if (len < (2+p.pl)) { -@@ -220,18 +222,30 @@ - /* - * Check that the integer length is what we expect it to be. If the - * handler want a 16 bits integer then a 32 bits is not good enough -+ * PV_INTEGER means that the handler is flexible. - */ - if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) { - ERROR(__FUNCTION__ "(), invalid parameter length! " - "Expected %d bytes, but value had %d bytes!\n", - type & PV_MASK, p.pl); - -- /* Skip parameter */ -- return p.pl+2; -+ /* Most parameters are bit/byte fields or little endian, -+ * so it's ok to only extract a subset of it (the subset -+ * that the handler expect). This is necessary, as some -+ * broken implementations seems to add extra undefined bits. -+ * If the parameter is shorter than we expect or is big -+ * endian, we can't play those tricks. Jean II */ -+ if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) { -+ /* Skip parameter */ -+ return p.pl+2; -+ } else { -+ /* Extract subset of it, fallthrough */ -+ extract_len = type & PV_MASK; -+ } - } - - -- switch (p.pl) { -+ switch (extract_len) { - case 1: - n += irda_param_unpack(buf+2, "b", &p.pv.i); - break; diff --git a/packages/linux/files/iw240_we15-6.diff b/packages/linux/files/iw240_we15-6.diff deleted file mode 100644 index 2ebfd8ec12..0000000000 --- a/packages/linux/files/iw240_we15-6.diff +++ /dev/null @@ -1,399 +0,0 @@ -diff -u -p linux/include/linux/wireless.14.h linux/include/linux/wireless.h ---- linux/include/linux/wireless.14.h Mon Dec 2 18:51:00 2002 -+++ linux/include/linux/wireless.h Mon Dec 2 18:53:35 2002 -@@ -1,7 +1,7 @@ - /* - * This file define a set of standard wireless extensions - * -- * Version : 14 25.1.02 -+ * Version : 15 12.7.02 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. -@@ -80,7 +80,7 @@ - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ --#define WIRELESS_EXT 14 -+#define WIRELESS_EXT 15 - - /* - * Changes : -@@ -153,17 +153,32 @@ - * - Define additional specific event numbers - * - Add "addr" and "param" fields in union iwreq_data - * - AP scanning stuff (SIOCSIWSCAN and friends) -+ * -+ * V14 to V15 -+ * ---------- -+ * - Add IW_PRIV_TYPE_ADDR for struct sockaddr private arg -+ * - Make struct iw_freq signed (both m & e), add explicit padding -+ * - Add IWEVCUSTOM for driver specific event/scanning token -+ * - Add IW_MAX_GET_SPY for driver returning a lot of addresses -+ * - Add IW_TXPOW_RANGE for range of Tx Powers -+ * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points -+ * - Add IW_MODE_MONITOR for passive monitor - */ - - /**************************** CONSTANTS ****************************/ - - /* -------------------------- IOCTL LIST -------------------------- */ - --/* Basic operations */ -+/* Wireless Identification */ - #define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ - #define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ --#define SIOCSIWNWID 0x8B02 /* set network id (the cell) */ --#define SIOCGIWNWID 0x8B03 /* get network id */ -+/* SIOCGIWNAME is used to verify the presence of Wireless Extensions. -+ * Common values : "IEEE 802.11-DS", "IEEE 802.11-FH", "IEEE 802.11b"... -+ * Don't put the name of your driver there, it's useless. */ -+ -+/* Basic operations */ -+#define SIOCSIWNWID 0x8B02 /* set network id (pre-802.11) */ -+#define SIOCGIWNWID 0x8B03 /* get network id (the cell) */ - #define SIOCSIWFREQ 0x8B04 /* set channel/frequency (Hz) */ - #define SIOCGIWFREQ 0x8B05 /* get channel/frequency (Hz) */ - #define SIOCSIWMODE 0x8B06 /* set operation mode */ -@@ -178,16 +193,18 @@ - #define SIOCGIWPRIV 0x8B0D /* get private ioctl interface info */ - #define SIOCSIWSTATS 0x8B0E /* Unused */ - #define SIOCGIWSTATS 0x8B0F /* Get /proc/net/wireless stats */ -+/* SIOCGIWSTATS is strictly used between user space and the kernel, and -+ * is never passed to the driver (i.e. the driver will never see it). */ - --/* Mobile IP support */ -+/* Mobile IP support (statistics per MAC address) */ - #define SIOCSIWSPY 0x8B10 /* set spy addresses */ - #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ - - /* Access Point manipulation */ - #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ - #define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ --#define SIOCGIWAPLIST 0x8B17 /* get list of access point in range */ --#define SIOCSIWSCAN 0x8B18 /* trigger scanning */ -+#define SIOCGIWAPLIST 0x8B17 /* Deprecated in favor of scanning */ -+#define SIOCSIWSCAN 0x8B18 /* trigger scanning (list cells) */ - #define SIOCGIWSCAN 0x8B19 /* get scanning results */ - - /* 802.11 specific support */ -@@ -197,9 +214,7 @@ - #define SIOCGIWNICKN 0x8B1D /* get node name/nickname */ - /* As the ESSID and NICKN are strings up to 32 bytes long, it doesn't fit - * within the 'iwreq' structure, so we need to use the 'data' member to -- * point to a string in user space, like it is done for RANGE... -- * The "flags" member indicate if the ESSID is active or not (promiscuous). -- */ -+ * point to a string in user space, like it is done for RANGE... */ - - /* Other parameters useful in 802.11 and some other devices */ - #define SIOCSIWRATE 0x8B20 /* set default bit rate (bps) */ -@@ -257,7 +272,10 @@ - /* Most events use the same identifier as ioctl requests */ - - #define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ --#define IWEVQUAL 0x8C01 /* Quality part of statistics */ -+#define IWEVQUAL 0x8C01 /* Quality part of statistics (scan) */ -+#define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ -+#define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ -+#define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ - - #define IWEVFIRST 0x8C00 - -@@ -273,7 +291,8 @@ - #define IW_PRIV_TYPE_BYTE 0x1000 /* Char as number */ - #define IW_PRIV_TYPE_CHAR 0x2000 /* Char as character */ - #define IW_PRIV_TYPE_INT 0x4000 /* 32 bits int */ --#define IW_PRIV_TYPE_FLOAT 0x5000 -+#define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ -+#define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ - - #define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */ - -@@ -297,13 +316,16 @@ - - /* Maximum tx powers in the range struct */ - #define IW_MAX_TXPOWER 8 -+/* Note : if you more than 8 TXPowers, just set the max and min or -+ * a few of them in the struct iw_range. */ - - /* Maximum of address that you may set with SPY */ --#define IW_MAX_SPY 8 -+#define IW_MAX_SPY 8 /* set */ -+#define IW_MAX_GET_SPY 64 /* get */ - - /* Maximum of address that you may get in the - list of access points in range */ --#define IW_MAX_AP 8 -+#define IW_MAX_AP 64 - - /* Maximum size of the ESSID and NICKN strings */ - #define IW_ESSID_MAX_SIZE 32 -@@ -315,6 +337,7 @@ - #define IW_MODE_MASTER 3 /* Synchronisation master or Access Point */ - #define IW_MODE_REPEAT 4 /* Wireless Repeater (forwarder) */ - #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ -+#define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ - - /* Maximum number of size of encoding token available - * they are listed in the range structure */ -@@ -350,8 +373,10 @@ - #define IW_POWER_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ - - /* Transmit Power flags available */ -+#define IW_TXPOW_TYPE 0x00FF /* Type of value */ - #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ - #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ -+#define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ - - /* Retry limits and lifetime flags available */ - #define IW_RETRY_ON 0x0000 /* No details... */ -@@ -376,6 +401,9 @@ - /* Maximum size of returned data */ - #define IW_SCAN_MAX_DATA 4096 /* In bytes */ - -+/* Max number of char in custom event - use multiple of them if needed */ -+#define IW_CUSTOM_MAX 256 /* In bytes */ -+ - /****************************** TYPES ******************************/ - - /* --------------------------- SUBTYPES --------------------------- */ -@@ -411,9 +439,10 @@ struct iw_point - */ - struct iw_freq - { -- __u32 m; /* Mantissa */ -- __u16 e; /* Exponent */ -+ __s32 m; /* Mantissa */ -+ __s16 e; /* Exponent */ - __u8 i; /* List index (when in range struct) */ -+ __u8 pad; /* Unused - just for alignement */ - }; - - /* -diff -u -p linux/include/net/iw_handler.14.h linux/include/net/iw_handler.h ---- linux/include/net/iw_handler.14.h Mon Dec 2 18:51:17 2002 -+++ linux/include/net/iw_handler.h Mon Dec 2 18:54:51 2002 -@@ -1,7 +1,7 @@ - /* - * This file define the new driver API for Wireless Extensions - * -- * Version : 3 17.1.02 -+ * Version : 4 21.6.02 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. -@@ -206,7 +206,7 @@ - * will be needed... - * I just plan to increment with each new version. - */ --#define IW_HANDLER_VERSION 3 -+#define IW_HANDLER_VERSION 4 - - /* - * Changes : -@@ -217,6 +217,9 @@ - * - Add Wireless Event support : - * o wireless_send_event() prototype - * o iwe_stream_add_event/point() inline functions -+ * V3 to V4 -+ * -------- -+ * - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes - */ - - /**************************** CONSTANTS ****************************/ -@@ -233,10 +236,10 @@ - #define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */ - #define IW_HEADER_TYPE_UINT 4 /* __u32 */ - #define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */ --#define IW_HEADER_TYPE_POINT 6 /* struct iw_point */ --#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */ --#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */ --#define IW_HEADER_TYPE_QUAL 9 /* struct iw_quality */ -+#define IW_HEADER_TYPE_ADDR 6 /* struct sockaddr */ -+#define IW_HEADER_TYPE_POINT 8 /* struct iw_point */ -+#define IW_HEADER_TYPE_PARAM 9 /* struct iw_param */ -+#define IW_HEADER_TYPE_QUAL 10 /* struct iw_quality */ - - /* Handling flags */ - /* Most are not implemented. I just use them as a reminder of some -diff -u -p linux/net/core/wireless.14.c linux/net/core/wireless.c ---- linux/net/core/wireless.14.c Mon Dec 2 18:51:35 2002 -+++ linux/net/core/wireless.c Mon Dec 2 18:53:10 2002 -@@ -33,8 +33,16 @@ - * o Propagate events as rtnetlink IFLA_WIRELESS option - * o Generate event on selected SET requests - * -- * v4 - 18.04.01 - Jean II -+ * v4 - 18.04.02 - Jean II - * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 -+ * -+ * v5 - 21.06.02 - Jean II -+ * o Add IW_PRIV_TYPE_ADDR in priv_type_size (+cleanup) -+ * o Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes -+ * o Add IWEVCUSTOM for driver specific event/scanning token -+ * o Turn on WE_STRICT_WRITE by default + kernel warning -+ * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) -+ * o Fix off-by-one in test (extra_size <= IFNAMSIZ) - */ - - /***************************** INCLUDES *****************************/ -@@ -50,8 +58,9 @@ - - /**************************** CONSTANTS ****************************/ - --/* This will be turned on later on... */ --#undef WE_STRICT_WRITE /* Check write buffer size */ -+/* Enough lenience, let's make sure things are proper... */ -+#define WE_STRICT_WRITE /* Check write buffer size */ -+/* I'll probably drop both the define and kernel message in the next version */ - - /* Debuging stuff */ - #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ -@@ -106,7 +115,7 @@ static const struct iw_ioctl_description - /* SIOCSIWSPY */ - { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0}, - /* SIOCGIWSPY */ -- { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0}, -+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_GET_SPY, 0}, - /* -- hole -- */ - { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, - /* -- hole -- */ -@@ -176,25 +185,41 @@ static const struct iw_ioctl_description - { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, - /* IWEVQUAL */ - { IW_HEADER_TYPE_QUAL, 0, 0, 0, 0, 0}, -+ /* IWEVCUSTOM */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_CUSTOM_MAX, 0}, -+ /* IWEVREGISTERED */ -+ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, -+ /* IWEVEXPIRED */ -+ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, - }; - static const int standard_event_num = (sizeof(standard_event) / - sizeof(struct iw_ioctl_description)); - - /* Size (in bytes) of the various private data types */ --static const char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; -+static const char priv_type_size[] = { -+ 0, /* IW_PRIV_TYPE_NONE */ -+ 1, /* IW_PRIV_TYPE_BYTE */ -+ 1, /* IW_PRIV_TYPE_CHAR */ -+ 0, /* Not defined */ -+ sizeof(__u32), /* IW_PRIV_TYPE_INT */ -+ sizeof(struct iw_freq), /* IW_PRIV_TYPE_FLOAT */ -+ sizeof(struct sockaddr), /* IW_PRIV_TYPE_ADDR */ -+ 0, /* Not defined */ -+}; - - /* Size (in bytes) of various events */ - static const int event_type_size[] = { -- IW_EV_LCP_LEN, -+ IW_EV_LCP_LEN, /* IW_HEADER_TYPE_NULL */ -+ 0, -+ IW_EV_CHAR_LEN, /* IW_HEADER_TYPE_CHAR */ - 0, -- IW_EV_CHAR_LEN, -+ IW_EV_UINT_LEN, /* IW_HEADER_TYPE_UINT */ -+ IW_EV_FREQ_LEN, /* IW_HEADER_TYPE_FREQ */ -+ IW_EV_ADDR_LEN, /* IW_HEADER_TYPE_ADDR */ - 0, -- IW_EV_UINT_LEN, -- IW_EV_FREQ_LEN, - IW_EV_POINT_LEN, /* Without variable payload */ -- IW_EV_PARAM_LEN, -- IW_EV_ADDR_LEN, -- IW_EV_QUAL_LEN, -+ IW_EV_PARAM_LEN, /* IW_HEADER_TYPE_PARAM */ -+ IW_EV_QUAL_LEN, /* IW_HEADER_TYPE_QUAL */ - }; - - /************************ COMMON SUBROUTINES ************************/ -@@ -440,8 +465,10 @@ static inline int ioctl_export_private(s - return -EFAULT; - #ifdef WE_STRICT_WRITE - /* Check if there is enough buffer up there */ -- if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1)) -+ if(iwr->u.data.length < dev->wireless_handlers->num_private_args) { -+ printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args); - return -E2BIG; -+ } - #endif /* WE_STRICT_WRITE */ - - /* Set the number of available ioctls. */ -@@ -471,6 +498,7 @@ static inline int ioctl_standard_call(st - const struct iw_ioctl_description * descr; - struct iw_request_info info; - int ret = -EINVAL; -+ int user_size = 0; - - /* Get the description of the IOCTL */ - if((cmd - SIOCIWFIRST) >= standard_ioctl_num) -@@ -518,11 +546,8 @@ static inline int ioctl_standard_call(st - /* Check NULL pointer */ - if(iwr->u.data.pointer == NULL) - return -EFAULT; --#ifdef WE_STRICT_WRITE -- /* Check if there is enough buffer up there */ -- if(iwr->u.data.length < descr->max_tokens) -- return -E2BIG; --#endif /* WE_STRICT_WRITE */ -+ /* Save user space buffer size for checking */ -+ user_size = iwr->u.data.length; - } - - #ifdef WE_IOCTL_DEBUG -@@ -559,6 +584,15 @@ static inline int ioctl_standard_call(st - - /* If we have something to return to the user */ - if (!ret && IW_IS_GET(cmd)) { -+#ifdef WE_STRICT_WRITE -+ /* Check if there is enough buffer up there */ -+ if(user_size < iwr->u.data.length) { -+ printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length); -+ kfree(extra); -+ return -E2BIG; -+ } -+#endif /* WE_STRICT_WRITE */ -+ - err = copy_to_user(iwr->u.data.pointer, extra, - iwr->u.data.length * - descr->token_size); -@@ -646,12 +680,18 @@ static inline int ioctl_private_call(str - /* Compute the size of the set/get arguments */ - if(descr != NULL) { - if(IW_IS_SET(cmd)) { -+ int offset = 0; /* For sub-ioctls */ -+ /* Check for sub-ioctl handler */ -+ if(descr->name[0] == '\0') -+ /* Reserve one int for sub-ioctl index */ -+ offset = sizeof(__u32); -+ - /* Size of set arguments */ - extra_size = get_priv_size(descr->set_args); - - /* Does it fits in iwr ? */ - if((descr->set_args & IW_PRIV_SIZE_FIXED) && -- (extra_size < IFNAMSIZ)) -+ ((extra_size + offset) <= IFNAMSIZ)) - extra_size = 0; - } else { - /* Size of set arguments */ -@@ -659,7 +699,7 @@ static inline int ioctl_private_call(str - - /* Does it fits in iwr ? */ - if((descr->get_args & IW_PRIV_SIZE_FIXED) && -- (extra_size < IFNAMSIZ)) -+ (extra_size <= IFNAMSIZ)) - extra_size = 0; - } - } -@@ -925,7 +965,7 @@ void wireless_send_event(struct net_devi - * The best the driver could do is to log an error message. - * We will do it ourselves instead... - */ -- printk(KERN_ERR "%s (WE) : Invalid Wireless Event (0x%04X)\n", -+ printk(KERN_ERR "%s (WE) : Invalid/Unknown Wireless Event (0x%04X)\n", - dev->name, cmd); - return; - } diff --git a/packages/linux/files/iw240_we18-5.diff b/packages/linux/files/iw240_we18-5.diff deleted file mode 100644 index f65987588d..0000000000 --- a/packages/linux/files/iw240_we18-5.diff +++ /dev/null @@ -1,421 +0,0 @@ -diff -u -p linux/include/linux/wireless.we17.h linux/include/linux/wireless.h ---- linux/include/linux/wireless.we17.h 2005-05-20 11:25:49.000000000 -0700 -+++ linux/include/linux/wireless.h 2005-05-20 11:29:05.000000000 -0700 -@@ -1,10 +1,10 @@ - /* - * This file define a set of standard wireless extensions - * -- * Version : 17 21.6.04 -+ * Version : 18 12.3.05 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. - */ - - #ifndef _LINUX_WIRELESS_H -@@ -82,7 +82,7 @@ - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ --#define WIRELESS_EXT 17 -+#define WIRELESS_EXT 18 - - /* - * Changes : -@@ -182,6 +182,21 @@ - * - Document (struct iw_quality *)->updated, add new flags (INVALID) - * - Wireless Event capability in struct iw_range - * - Add support for relative TxPower (yick !) -+ * -+ * V17 to V18 (From Jouni Malinen <jkmaline@cc.hut.fi>) -+ * ---------- -+ * - Add support for WPA/WPA2 -+ * - Add extended encoding configuration (SIOCSIWENCODEEXT and -+ * SIOCGIWENCODEEXT) -+ * - Add SIOCSIWGENIE/SIOCGIWGENIE -+ * - Add SIOCSIWMLME -+ * - Add SIOCSIWPMKSA -+ * - Add struct iw_range bit field for supported encoding capabilities -+ * - Add optional scan request parameters for SIOCSIWSCAN -+ * - Add SIOCSIWAUTH/SIOCGIWAUTH for setting authentication and WPA -+ * related parameters (extensible up to 4096 parameter values) -+ * - Add wireless events: IWEVGENIE, IWEVMICHAELMICFAILURE, -+ * IWEVASSOCREQIE, IWEVASSOCRESPIE, IWEVPMKIDCAND - */ - - /**************************** CONSTANTS ****************************/ -@@ -256,6 +271,30 @@ - #define SIOCSIWPOWER 0x8B2C /* set Power Management settings */ - #define SIOCGIWPOWER 0x8B2D /* get Power Management settings */ - -+/* WPA : Generic IEEE 802.11 informatiom element (e.g., for WPA/RSN/WMM). -+ * This ioctl uses struct iw_point and data buffer that includes IE id and len -+ * fields. More than one IE may be included in the request. Setting the generic -+ * IE to empty buffer (len=0) removes the generic IE from the driver. Drivers -+ * are allowed to generate their own WPA/RSN IEs, but in these cases, drivers -+ * are required to report the used IE as a wireless event, e.g., when -+ * associating with an AP. */ -+#define SIOCSIWGENIE 0x8B30 /* set generic IE */ -+#define SIOCGIWGENIE 0x8B31 /* get generic IE */ -+ -+/* WPA : IEEE 802.11 MLME requests */ -+#define SIOCSIWMLME 0x8B16 /* request MLME operation; uses -+ * struct iw_mlme */ -+/* WPA : Authentication mode parameters */ -+#define SIOCSIWAUTH 0x8B32 /* set authentication mode params */ -+#define SIOCGIWAUTH 0x8B33 /* get authentication mode params */ -+ -+/* WPA : Extended version of encoding configuration */ -+#define SIOCSIWENCODEEXT 0x8B34 /* set encoding token & mode */ -+#define SIOCGIWENCODEEXT 0x8B35 /* get encoding token & mode */ -+ -+/* WPA2 : PMKSA cache management */ -+#define SIOCSIWPMKSA 0x8B36 /* PMKSA cache operation */ -+ - /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ - - /* These 32 ioctl are wireless device private, for 16 commands. -@@ -297,6 +336,34 @@ - #define IWEVCUSTOM 0x8C02 /* Driver specific ascii string */ - #define IWEVREGISTERED 0x8C03 /* Discovered a new node (AP mode) */ - #define IWEVEXPIRED 0x8C04 /* Expired a node (AP mode) */ -+#define IWEVGENIE 0x8C05 /* Generic IE (WPA, RSN, WMM, ..) -+ * (scan results); This includes id and -+ * length fields. One IWEVGENIE may -+ * contain more than one IE. Scan -+ * results may contain one or more -+ * IWEVGENIE events. */ -+#define IWEVMICHAELMICFAILURE 0x8C06 /* Michael MIC failure -+ * (struct iw_michaelmicfailure) -+ */ -+#define IWEVASSOCREQIE 0x8C07 /* IEs used in (Re)Association Request. -+ * The data includes id and length -+ * fields and may contain more than one -+ * IE. This event is required in -+ * Managed mode if the driver -+ * generates its own WPA/RSN IE. This -+ * should be sent just before -+ * IWEVREGISTERED event for the -+ * association. */ -+#define IWEVASSOCRESPIE 0x8C08 /* IEs used in (Re)Association -+ * Response. The data includes id and -+ * length fields and may contain more -+ * than one IE. This may be sent -+ * between IWEVASSOCREQIE and -+ * IWEVREGISTERED events for the -+ * association. */ -+#define IWEVPMKIDCAND 0x8C09 /* PMKID candidate for RSN -+ * pre-authentication -+ * (struct iw_pmkid_cand) */ - - #define IWEVFIRST 0x8C00 - -@@ -432,12 +499,94 @@ - #define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ - #define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ - #define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ -+/* struct iw_scan_req scan_type */ -+#define IW_SCAN_TYPE_ACTIVE 0 -+#define IW_SCAN_TYPE_PASSIVE 1 - /* Maximum size of returned data */ - #define IW_SCAN_MAX_DATA 4096 /* In bytes */ - - /* Max number of char in custom event - use multiple of them if needed */ - #define IW_CUSTOM_MAX 256 /* In bytes */ - -+/* Generic information element */ -+#define IW_GENERIC_IE_MAX 1024 -+ -+/* MLME requests (SIOCSIWMLME / struct iw_mlme) */ -+#define IW_MLME_DEAUTH 0 -+#define IW_MLME_DISASSOC 1 -+ -+/* SIOCSIWAUTH/SIOCGIWAUTH struct iw_param flags */ -+#define IW_AUTH_INDEX 0x0FFF -+#define IW_AUTH_FLAGS 0xF000 -+/* SIOCSIWAUTH/SIOCGIWAUTH parameters (0 .. 4095) -+ * (IW_AUTH_INDEX mask in struct iw_param flags; this is the index of the -+ * parameter that is being set/get to; value will be read/written to -+ * struct iw_param value field) */ -+#define IW_AUTH_WPA_VERSION 0 -+#define IW_AUTH_CIPHER_PAIRWISE 1 -+#define IW_AUTH_CIPHER_GROUP 2 -+#define IW_AUTH_KEY_MGMT 3 -+#define IW_AUTH_TKIP_COUNTERMEASURES 4 -+#define IW_AUTH_DROP_UNENCRYPTED 5 -+#define IW_AUTH_80211_AUTH_ALG 6 -+#define IW_AUTH_WPA_ENABLED 7 -+#define IW_AUTH_RX_UNENCRYPTED_EAPOL 8 -+#define IW_AUTH_ROAMING_CONTROL 9 -+#define IW_AUTH_PRIVACY_INVOKED 10 -+ -+/* IW_AUTH_WPA_VERSION values (bit field) */ -+#define IW_AUTH_WPA_VERSION_DISABLED 0x00000001 -+#define IW_AUTH_WPA_VERSION_WPA 0x00000002 -+#define IW_AUTH_WPA_VERSION_WPA2 0x00000004 -+ -+/* IW_AUTH_PAIRWISE_CIPHER and IW_AUTH_GROUP_CIPHER values (bit field) */ -+#define IW_AUTH_CIPHER_NONE 0x00000001 -+#define IW_AUTH_CIPHER_WEP40 0x00000002 -+#define IW_AUTH_CIPHER_TKIP 0x00000004 -+#define IW_AUTH_CIPHER_CCMP 0x00000008 -+#define IW_AUTH_CIPHER_WEP104 0x00000010 -+ -+/* IW_AUTH_KEY_MGMT values (bit field) */ -+#define IW_AUTH_KEY_MGMT_802_1X 1 -+#define IW_AUTH_KEY_MGMT_PSK 2 -+ -+/* IW_AUTH_80211_AUTH_ALG values (bit field) */ -+#define IW_AUTH_ALG_OPEN_SYSTEM 0x00000001 -+#define IW_AUTH_ALG_SHARED_KEY 0x00000002 -+#define IW_AUTH_ALG_LEAP 0x00000004 -+ -+/* IW_AUTH_ROAMING_CONTROL values */ -+#define IW_AUTH_ROAMING_ENABLE 0 /* driver/firmware based roaming */ -+#define IW_AUTH_ROAMING_DISABLE 1 /* user space program used for roaming -+ * control */ -+ -+/* SIOCSIWENCODEEXT definitions */ -+#define IW_ENCODE_SEQ_MAX_SIZE 8 -+/* struct iw_encode_ext ->alg */ -+#define IW_ENCODE_ALG_NONE 0 -+#define IW_ENCODE_ALG_WEP 1 -+#define IW_ENCODE_ALG_TKIP 2 -+#define IW_ENCODE_ALG_CCMP 3 -+/* struct iw_encode_ext ->ext_flags */ -+#define IW_ENCODE_EXT_TX_SEQ_VALID 0x00000001 -+#define IW_ENCODE_EXT_RX_SEQ_VALID 0x00000002 -+#define IW_ENCODE_EXT_GROUP_KEY 0x00000004 -+#define IW_ENCODE_EXT_SET_TX_KEY 0x00000008 -+ -+/* IWEVMICHAELMICFAILURE : struct iw_michaelmicfailure ->flags */ -+#define IW_MICFAILURE_KEY_ID 0x00000003 /* Key ID 0..3 */ -+#define IW_MICFAILURE_GROUP 0x00000004 -+#define IW_MICFAILURE_PAIRWISE 0x00000008 -+#define IW_MICFAILURE_STAKEY 0x00000010 -+#define IW_MICFAILURE_COUNT 0x00000060 /* 1 or 2 (0 = count not supported) -+ */ -+ -+/* Bit field values for enc_capa in struct iw_range */ -+#define IW_ENC_CAPA_WPA 0x00000001 -+#define IW_ENC_CAPA_WPA2 0x00000002 -+#define IW_ENC_CAPA_CIPHER_TKIP 0x00000004 -+#define IW_ENC_CAPA_CIPHER_CCMP 0x00000008 -+ - /* Event capability macros - in (struct iw_range *)->event_capa - * Because we have more than 32 possible events, we use an array of - * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ -@@ -546,6 +695,132 @@ struct iw_thrspy - struct iw_quality high; /* High threshold */ - }; - -+/* -+ * Optional data for scan request -+ * -+ * Note: these optional parameters are controlling parameters for the -+ * scanning behavior, these do not apply to getting scan results -+ * (SIOCGIWSCAN). Drivers are expected to keep a local BSS table and -+ * provide a merged results with all BSSes even if the previous scan -+ * request limited scanning to a subset, e.g., by specifying an SSID. -+ * Especially, scan results are required to include an entry for the -+ * current BSS if the driver is in Managed mode and associated with an AP. -+ */ -+struct iw_scan_req -+{ -+ __u8 scan_type; /* IW_SCAN_TYPE_{ACTIVE,PASSIVE} */ -+ __u8 essid_len; -+ __u8 num_channels; /* num entries in channel_list; -+ * 0 = scan all allowed channels */ -+ __u8 flags; /* reserved as padding; use zero, this may -+ * be used in the future for adding flags -+ * to request different scan behavior */ -+ struct sockaddr bssid; /* ff:ff:ff:ff:ff:ff for broadcast BSSID or -+ * individual address of a specific BSS */ -+ -+ /* -+ * Use this ESSID if IW_SCAN_THIS_ESSID flag is used instead of using -+ * the current ESSID. This allows scan requests for specific ESSID -+ * without having to change the current ESSID and potentially breaking -+ * the current association. -+ */ -+ __u8 essid[IW_ESSID_MAX_SIZE]; -+ -+ /* -+ * Optional parameters for changing the default scanning behavior. -+ * These are based on the MLME-SCAN.request from IEEE Std 802.11. -+ * TU is 1.024 ms. If these are set to 0, driver is expected to use -+ * reasonable default values. min_channel_time defines the time that -+ * will be used to wait for the first reply on each channel. If no -+ * replies are received, next channel will be scanned after this. If -+ * replies are received, total time waited on the channel is defined by -+ * max_channel_time. -+ */ -+ __u32 min_channel_time; /* in TU */ -+ __u32 max_channel_time; /* in TU */ -+ -+ struct iw_freq channel_list[IW_MAX_FREQUENCIES]; -+}; -+ -+/* ------------------------- WPA SUPPORT ------------------------- */ -+ -+/* -+ * Extended data structure for get/set encoding (this is used with -+ * SIOCSIWENCODEEXT/SIOCGIWENCODEEXT. struct iw_point and IW_ENCODE_* -+ * flags are used in the same way as with SIOCSIWENCODE/SIOCGIWENCODE and -+ * only the data contents changes (key data -> this structure, including -+ * key data). -+ * -+ * If the new key is the first group key, it will be set as the default -+ * TX key. Otherwise, default TX key index is only changed if -+ * IW_ENCODE_EXT_SET_TX_KEY flag is set. -+ * -+ * Key will be changed with SIOCSIWENCODEEXT in all cases except for -+ * special "change TX key index" operation which is indicated by setting -+ * key_len = 0 and ext_flags |= IW_ENCODE_EXT_SET_TX_KEY. -+ * -+ * tx_seq/rx_seq are only used when respective -+ * IW_ENCODE_EXT_{TX,RX}_SEQ_VALID flag is set in ext_flags. Normal -+ * TKIP/CCMP operation is to set RX seq with SIOCSIWENCODEEXT and start -+ * TX seq from zero whenever key is changed. SIOCGIWENCODEEXT is normally -+ * used only by an Authenticator (AP or an IBSS station) to get the -+ * current TX sequence number. Using TX_SEQ_VALID for SIOCSIWENCODEEXT and -+ * RX_SEQ_VALID for SIOCGIWENCODEEXT are optional, but can be useful for -+ * debugging/testing. -+ */ -+struct iw_encode_ext -+{ -+ __u32 ext_flags; /* IW_ENCODE_EXT_* */ -+ __u8 tx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ -+ __u8 rx_seq[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ -+ struct sockaddr addr; /* ff:ff:ff:ff:ff:ff for broadcast/multicast -+ * (group) keys or unicast address for -+ * individual keys */ -+ __u16 alg; /* IW_ENCODE_ALG_* */ -+ __u16 key_len; -+ __u8 key[0]; -+}; -+ -+/* SIOCSIWMLME data */ -+struct iw_mlme -+{ -+ __u16 cmd; /* IW_MLME_* */ -+ __u16 reason_code; -+ struct sockaddr addr; -+}; -+ -+/* SIOCSIWPMKSA data */ -+#define IW_PMKSA_ADD 1 -+#define IW_PMKSA_REMOVE 2 -+#define IW_PMKSA_FLUSH 3 -+ -+#define IW_PMKID_LEN 16 -+ -+struct iw_pmksa -+{ -+ __u32 cmd; /* IW_PMKSA_* */ -+ struct sockaddr bssid; -+ __u8 pmkid[IW_PMKID_LEN]; -+}; -+ -+/* IWEVMICHAELMICFAILURE data */ -+struct iw_michaelmicfailure -+{ -+ __u32 flags; -+ struct sockaddr src_addr; -+ __u8 tsc[IW_ENCODE_SEQ_MAX_SIZE]; /* LSB first */ -+}; -+ -+/* IWEVPMKIDCAND data */ -+#define IW_PMKID_CAND_PREAUTH 0x00000001 /* RNS pre-authentication enabled */ -+struct iw_pmkid_cand -+{ -+ __u32 flags; /* IW_PMKID_CAND_* */ -+ __u32 index; /* the smaller the index, the higher the -+ * priority */ -+ struct sockaddr bssid; -+}; -+ - /* ------------------------ WIRELESS STATS ------------------------ */ - /* - * Wireless statistics (used for /proc/net/wireless) -@@ -725,6 +1000,8 @@ struct iw_range - struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ - /* Note : this frequency list doesn't need to fit channel numbers, - * because each entry contain its channel index */ -+ -+ __u32 enc_capa; /* IW_ENC_CAPA_* bit field */ - }; - - /* -diff -u -p linux/net/core/wireless.we17.c linux/net/core/wireless.c ---- linux/net/core/wireless.we17.c 2005-05-20 11:26:11.000000000 -0700 -+++ linux/net/core/wireless.c 2005-05-20 15:37:09.000000000 -0700 -@@ -2,7 +2,7 @@ - * This file implement the Wireless Extensions APIs. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2005 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ -@@ -137,12 +137,12 @@ static const struct iw_ioctl_description - { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, - /* SIOCGIWAP */ - { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -- /* -- hole -- */ -- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCSIWMLME */ -+ { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_mlme), sizeof(struct iw_mlme), 0}, - /* SIOCGIWAPLIST */ - { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, IW_DESCR_FLAG_NOMAX}, - /* SIOCSIWSCAN */ -- { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_scan_req), 0}, - /* SIOCGIWSCAN */ - { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, IW_DESCR_FLAG_NOMAX}, - /* SIOCSIWESSID */ -@@ -185,6 +185,25 @@ static const struct iw_ioctl_description - { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, - /* SIOCGIWPOWER */ - { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCSIWGENIE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, -+ /* SIOCGIWGENIE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, -+ /* SIOCSIWAUTH */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWAUTH */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCSIWENCODEEXT */ -+ { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_encode_ext), sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, 0}, -+ /* SIOCGIWENCODEEXT */ -+ { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_encode_ext), sizeof(struct iw_encode_ext) + IW_ENCODING_TOKEN_MAX, 0}, -+ /* SIOCSIWPMKSA */ -+ { IW_HEADER_TYPE_POINT, 0, 1, sizeof(struct iw_pmksa), sizeof(struct iw_pmksa), 0}, -+ /* -- hole -- */ - }; - static const int standard_ioctl_num = (sizeof(standard_ioctl) / - sizeof(struct iw_ioctl_description)); -@@ -204,6 +223,16 @@ static const struct iw_ioctl_description - { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, - /* IWEVEXPIRED */ - { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, -+ /* IWEVGENIE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, -+ /* IWEVMICHAELMICFAILURE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_michaelmicfailure), 0}, -+ /* IWEVASSOCREQIE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, -+ /* IWEVASSOCRESPIE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_GENERIC_IE_MAX, 0}, -+ /* IWEVPMKIDCAND */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_pmkid_cand), 0}, - }; - static const int standard_event_num = (sizeof(standard_event) / - sizeof(struct iw_ioctl_description)); diff --git a/packages/linux/files/iw241_we16-6.diff b/packages/linux/files/iw241_we16-6.diff deleted file mode 100644 index 71cb4c08f6..0000000000 --- a/packages/linux/files/iw241_we16-6.diff +++ /dev/null @@ -1,667 +0,0 @@ -diff -u -p linux/include/linux/wireless.15.h linux/include/linux/wireless.h ---- linux/include/linux/wireless.15.h Fri Jan 10 16:55:07 2003 -+++ linux/include/linux/wireless.h Wed Apr 2 16:33:31 2003 -@@ -1,7 +1,7 @@ - /* - * This file define a set of standard wireless extensions - * -- * Version : 15 12.7.02 -+ * Version : 16 2.4.03 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. -@@ -69,6 +69,8 @@ - - /***************************** INCLUDES *****************************/ - -+/* To minimise problems in user space, I might remove those headers -+ * at some point. Jean II */ - #include <linux/types.h> /* for "caddr_t" et al */ - #include <linux/socket.h> /* for "struct sockaddr" et al */ - #include <linux/if.h> /* for IFNAMSIZ and co... */ -@@ -80,7 +82,7 @@ - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ --#define WIRELESS_EXT 15 -+#define WIRELESS_EXT 16 - - /* - * Changes : -@@ -163,6 +165,16 @@ - * - Add IW_TXPOW_RANGE for range of Tx Powers - * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points - * - Add IW_MODE_MONITOR for passive monitor -+ * -+ * V15 to V16 -+ * ---------- -+ * - Increase the number of bitrates in iw_range to 32 (for 802.11g) -+ * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) -+ * - Reshuffle struct iw_range for increases, add filler -+ * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses -+ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support -+ * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" -+ * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index - */ - - /**************************** CONSTANTS ****************************/ -@@ -196,9 +208,11 @@ - /* SIOCGIWSTATS is strictly used between user space and the kernel, and - * is never passed to the driver (i.e. the driver will never see it). */ - --/* Mobile IP support (statistics per MAC address) */ -+/* Spy support (statistics per MAC address - used for Mobile IP support) */ - #define SIOCSIWSPY 0x8B10 /* set spy addresses */ - #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ -+#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ -+#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ - - /* Access Point manipulation */ - #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ -@@ -294,7 +308,7 @@ - #define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ - #define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ - --#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */ -+#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ - - #define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ - -@@ -306,13 +320,13 @@ - /* ----------------------- OTHER CONSTANTS ----------------------- */ - - /* Maximum frequencies in the range struct */ --#define IW_MAX_FREQUENCIES 16 -+#define IW_MAX_FREQUENCIES 32 - /* Note : if you have something like 80 frequencies, - * don't increase this constant and don't fill the frequency list. - * The user will be able to set by channel anyway... */ - - /* Maximum bit rates in the range struct */ --#define IW_MAX_BITRATES 8 -+#define IW_MAX_BITRATES 32 - - /* Maximum tx powers in the range struct */ - #define IW_MAX_TXPOWER 8 -@@ -320,8 +334,7 @@ - * a few of them in the struct iw_range. */ - - /* Maximum of address that you may set with SPY */ --#define IW_MAX_SPY 8 /* set */ --#define IW_MAX_GET_SPY 64 /* get */ -+#define IW_MAX_SPY 8 - - /* Maximum of address that you may get in the - list of access points in range */ -@@ -354,7 +367,8 @@ - #define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ - #define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ - #define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ --#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ -+#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ -+#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ - - /* Power management flags available (along with the value, if any) */ - #define IW_POWER_ON 0x0000 /* No details... */ -@@ -482,6 +496,17 @@ struct iw_missed - __u32 beacon; /* Missed beacons/superframe */ - }; - -+/* -+ * Quality range (for spy threshold) -+ */ -+struct iw_thrspy -+{ -+ struct sockaddr addr; /* Source address (hw/mac) */ -+ struct iw_quality qual; /* Quality of the link */ -+ struct iw_quality low; /* Low threshold */ -+ struct iw_quality high; /* High threshold */ -+}; -+ - /* ------------------------ WIRELESS STATS ------------------------ */ - /* - * Wireless statistics (used for /proc/net/wireless) -@@ -534,7 +559,7 @@ union iwreq_data - struct iw_quality qual; /* Quality part of statistics */ - - struct sockaddr ap_addr; /* Access point address */ -- struct sockaddr addr; /* Destination address (hw) */ -+ struct sockaddr addr; /* Destination address (hw/mac) */ - - struct iw_param param; /* Other small parameters */ - struct iw_point data; /* Other large parameters */ -@@ -582,17 +607,31 @@ struct iw_range - __u32 min_nwid; /* Minimal NWID we are able to set */ - __u32 max_nwid; /* Maximal NWID we are able to set */ - -- /* Frequency */ -- __u16 num_channels; /* Number of channels [0; num - 1] */ -- __u8 num_frequency; /* Number of entry in the list */ -- struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ -- /* Note : this frequency list doesn't need to fit channel numbers */ -+ /* Old Frequency (backward compat - moved lower ) */ -+ __u16 old_num_channels; -+ __u8 old_num_frequency; -+ /* Filler to keep "version" at the same offset */ -+ __s32 old_freq[6]; - - /* signal level threshold range */ - __s32 sensitivity; - - /* Quality of link & SNR stuff */ -+ /* Quality range (link, level, noise) -+ * If the quality is absolute, it will be in the range [0 ; max_qual], -+ * if the quality is dBm, it will be in the range [max_qual ; 0]. -+ * Don't forget that we use 8 bit arithmetics... */ - struct iw_quality max_qual; /* Quality of the link */ -+ /* This should contain the average/typical values of the quality -+ * indicator. This should be the threshold between a "good" and -+ * a "bad" link (example : monitor going from green to orange). -+ * Currently, user space apps like quality monitors don't have any -+ * way to calibrate the measurement. With this, they can split -+ * the range between 0 and max_qual in different quality level -+ * (using a geometric subdivision centered on the average). -+ * I expect that people doing the user space apps will feedback -+ * us on which value we need to put in each driver... */ -+ struct iw_quality avg_qual; /* Quality of the link */ - - /* Rates */ - __u8 num_bitrates; /* Number of entries in the list */ -@@ -619,6 +658,8 @@ struct iw_range - __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ - __u8 num_encoding_sizes; /* Number of entry in the list */ - __u8 max_encoding_tokens; /* Max number of tokens */ -+ /* For drivers that need a "login/passwd" form */ -+ __u8 encoding_login_index; /* token index for login token */ - - /* Transmit power */ - __u16 txpower_capa; /* What options are supported */ -@@ -638,18 +679,12 @@ struct iw_range - __s32 min_r_time; /* Minimal retry lifetime */ - __s32 max_r_time; /* Maximal retry lifetime */ - -- /* Average quality of link & SNR */ -- struct iw_quality avg_qual; /* Quality of the link */ -- /* This should contain the average/typical values of the quality -- * indicator. This should be the threshold between a "good" and -- * a "bad" link (example : monitor going from green to orange). -- * Currently, user space apps like quality monitors don't have any -- * way to calibrate the measurement. With this, they can split -- * the range between 0 and max_qual in different quality level -- * (using a geometric subdivision centered on the average). -- * I expect that people doing the user space apps will feedback -- * us on which value we need to put in each driver... -- */ -+ /* Frequency */ -+ __u16 num_channels; /* Number of channels [0; num - 1] */ -+ __u8 num_frequency; /* Number of entry in the list */ -+ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ -+ /* Note : this frequency list doesn't need to fit channel numbers, -+ * because each entry contain its channel index */ - }; - - /* -diff -u -p linux/include/net/iw_handler.15.h linux/include/net/iw_handler.h ---- linux/include/net/iw_handler.15.h Fri Jan 10 16:55:17 2003 -+++ linux/include/net/iw_handler.h Fri Jan 10 17:02:13 2003 -@@ -1,7 +1,7 @@ - /* - * This file define the new driver API for Wireless Extensions - * -- * Version : 4 21.6.02 -+ * Version : 5 4.12.02 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. -@@ -206,7 +206,7 @@ - * will be needed... - * I just plan to increment with each new version. - */ --#define IW_HANDLER_VERSION 4 -+#define IW_HANDLER_VERSION 5 - - /* - * Changes : -@@ -220,10 +220,18 @@ - * V3 to V4 - * -------- - * - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes -+ * -+ * V4 to V5 -+ * -------- -+ * - Add new spy support : struct iw_spy_data & prototypes - */ - - /**************************** CONSTANTS ****************************/ - -+/* Enable enhanced spy support. Disable to reduce footprint */ -+#define IW_WIRELESS_SPY -+#define IW_WIRELESS_THRSPY -+ - /* Special error message for the driver to indicate that we - * should do a commit after return from the iw_handler */ - #define EIWCOMMIT EINPROGRESS -@@ -315,6 +323,9 @@ struct iw_handler_def - * We will automatically export that to user space... */ - struct iw_priv_args * private_args; - -+ /* Driver enhanced spy support */ -+ long spy_offset; /* Spy data offset */ -+ - /* In the long term, get_wireless_stats will move from - * 'struct net_device' to here, to minimise bloat. */ - }; -@@ -350,6 +361,33 @@ struct iw_ioctl_description - - /* Need to think of short header translation table. Later. */ - -+/* --------------------- ENHANCED SPY SUPPORT --------------------- */ -+/* -+ * In the old days, the driver was handling spy support all by itself. -+ * Now, the driver can delegate this task to Wireless Extensions. -+ * It needs to include this struct in its private part and use the -+ * standard spy iw_handler. -+ */ -+ -+/* -+ * Instance specific spy data, i.e. addresses spied and quality for them. -+ */ -+struct iw_spy_data -+{ -+#ifdef IW_WIRELESS_SPY -+ /* --- Standard spy support --- */ -+ int spy_number; -+ u_char spy_address[IW_MAX_SPY][ETH_ALEN]; -+ struct iw_quality spy_stat[IW_MAX_SPY]; -+#ifdef IW_WIRELESS_THRSPY -+ /* --- Enhanced spy support (event) */ -+ struct iw_quality spy_thr_low; /* Low threshold */ -+ struct iw_quality spy_thr_high; /* High threshold */ -+ u_char spy_thr_under[IW_MAX_SPY]; -+#endif /* IW_WIRELESS_THRSPY */ -+#endif /* IW_WIRELESS_SPY */ -+}; -+ - /**************************** PROTOTYPES ****************************/ - /* - * Functions part of the Wireless Extensions (defined in net/core/wireless.c). -@@ -375,6 +413,31 @@ extern void wireless_send_event(struct n - - /* We may need a function to send a stream of events to user space. - * More on that later... */ -+ -+/* Standard handler for SIOCSIWSPY */ -+extern int iw_handler_set_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Standard handler for SIOCGIWSPY */ -+extern int iw_handler_get_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Standard handler for SIOCSIWTHRSPY */ -+extern int iw_handler_set_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Standard handler for SIOCGIWTHRSPY */ -+extern int iw_handler_get_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Driver call to update spy records */ -+extern void wireless_spy_update(struct net_device * dev, -+ unsigned char * address, -+ struct iw_quality * wstats); - - /************************* INLINE FUNTIONS *************************/ - /* -diff -u -p linux/net/core/wireless.15.c linux/net/core/wireless.c ---- linux/net/core/wireless.15.c Fri Jan 10 16:56:16 2003 -+++ linux/net/core/wireless.c Fri Jan 10 16:59:55 2003 -@@ -2,7 +2,7 @@ - * This file implement the Wireless Extensions APIs. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ -@@ -43,6 +43,11 @@ - * o Turn on WE_STRICT_WRITE by default + kernel warning - * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) - * o Fix off-by-one in test (extra_size <= IFNAMSIZ) -+ * -+ * v6 - 9.01.03 - Jean II -+ * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() -+ * o Add enhanced spy support : iw_handler_set_thrspy() and event. -+ * o Add WIRELESS_EXT version display in /proc/net/wireless - */ - - /***************************** INCLUDES *****************************/ -@@ -52,6 +57,7 @@ - #include <linux/types.h> /* off_t */ - #include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ - #include <linux/rtnetlink.h> /* rtnetlink stuff */ -+#include <linux/if_arp.h> /* ARPHRD_ETHER */ - - #include <linux/wireless.h> /* Pretty obvious */ - #include <net/iw_handler.h> /* New driver API */ -@@ -65,6 +71,7 @@ - /* Debuging stuff */ - #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ - #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ -+#undef WE_SPY_DEBUG /* Debug enhanced spy support */ - - /* Options */ - #define WE_EVENT_NETLINK /* Propagate events using rtnetlink */ -@@ -72,7 +79,7 @@ - - /************************* GLOBAL VARIABLES *************************/ - /* -- * You should not use global variables, because or re-entrancy. -+ * You should not use global variables, because of re-entrancy. - * On our case, it's only const, so it's OK... - */ - /* -@@ -115,11 +122,11 @@ static const struct iw_ioctl_description - /* SIOCSIWSPY */ - { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0}, - /* SIOCGIWSPY */ -- { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_GET_SPY, 0}, -- /* -- hole -- */ -- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -- /* -- hole -- */ -- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0}, -+ /* SIOCSIWTHRSPY */ -+ { IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0}, -+ /* SIOCGIWTHRSPY */ -+ { IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0}, - /* SIOCSIWAP */ - { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, - /* SIOCGIWAP */ -@@ -377,9 +384,9 @@ int dev_get_wireless_info(char * buffer, - struct net_device * dev; - - size = sprintf(buffer, -- "Inter-| sta-| Quality | Discarded packets | Missed\n" -- " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" -- ); -+ "Inter-| sta-| Quality | Discarded packets | Missed | WE\n" -+ " face | tus | link level noise | nwid crypt frag retry misc | beacon | %d\n", -+ WIRELESS_EXT); - - pos += size; - len += size; -@@ -1023,4 +1030,253 @@ void wireless_send_event(struct net_devi - kfree(event); - - return; /* Always success, I guess ;-) */ -+} -+ -+/********************** ENHANCED IWSPY SUPPORT **********************/ -+/* -+ * In the old days, the driver was handling spy support all by itself. -+ * Now, the driver can delegate this task to Wireless Extensions. -+ * It needs to use those standard spy iw_handler in struct iw_handler_def, -+ * push data to us via XXX and include struct iw_spy_data in its -+ * private part. -+ * One of the main advantage of centralising spy support here is that -+ * it becomes much easier to improve and extend it without having to touch -+ * the drivers. One example is the addition of the Spy-Threshold events. -+ * Note : IW_WIRELESS_SPY is defined in iw_handler.h -+ */ -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : set Spy List -+ */ -+int iw_handler_set_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_SPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct sockaddr * address = (struct sockaddr *) extra; -+ -+ /* Disable spy collection while we copy the addresses. -+ * As we don't disable interrupts, we need to do this to avoid races. -+ * As we are the only writer, this is good enough. */ -+ spydata->spy_number = 0; -+ -+ /* Are there are addresses to copy? */ -+ if(wrqu->data.length > 0) { -+ int i; -+ -+ /* Copy addresses */ -+ for(i = 0; i < wrqu->data.length; i++) -+ memcpy(spydata->spy_address[i], address[i].sa_data, -+ ETH_ALEN); -+ /* Reset stats */ -+ memset(spydata->spy_stat, 0, -+ sizeof(struct iw_quality) * IW_MAX_SPY); -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "iw_handler_set_spy() : offset %ld, spydata %p, num %d\n", dev->wireless_handlers->spy_offset, spydata, wrqu->data.length); -+ for (i = 0; i < wrqu->data.length; i++) -+ printk(KERN_DEBUG -+ "%02X:%02X:%02X:%02X:%02X:%02X \n", -+ spydata->spy_address[i][0], -+ spydata->spy_address[i][1], -+ spydata->spy_address[i][2], -+ spydata->spy_address[i][3], -+ spydata->spy_address[i][4], -+ spydata->spy_address[i][5]); -+#endif /* WE_SPY_DEBUG */ -+ } -+ /* Enable addresses */ -+ spydata->spy_number = wrqu->data.length; -+ -+ return 0; -+#else /* IW_WIRELESS_SPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_SPY */ -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : get Spy List -+ */ -+int iw_handler_get_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_SPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct sockaddr * address = (struct sockaddr *) extra; -+ int i; -+ -+ wrqu->data.length = spydata->spy_number; -+ -+ /* Copy addresses. */ -+ for(i = 0; i < spydata->spy_number; i++) { -+ memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); -+ address[i].sa_family = AF_UNIX; -+ } -+ /* Copy stats to the user buffer (just after). */ -+ if(spydata->spy_number > 0) -+ memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), -+ spydata->spy_stat, -+ sizeof(struct iw_quality) * spydata->spy_number); -+ /* Reset updated flags. */ -+ for(i = 0; i < spydata->spy_number; i++) -+ spydata->spy_stat[i].updated = 0; -+ return 0; -+#else /* IW_WIRELESS_SPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_SPY */ -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : set spy threshold -+ */ -+int iw_handler_set_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_THRSPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra; -+ -+ /* Just do it */ -+ memcpy(&(spydata->spy_thr_low), &(threshold->low), -+ 2 * sizeof(struct iw_quality)); -+ -+ /* Clear flag */ -+ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level); -+#endif /* WE_SPY_DEBUG */ -+ -+ return 0; -+#else /* IW_WIRELESS_THRSPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_THRSPY */ -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : get spy threshold -+ */ -+int iw_handler_get_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_THRSPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra; -+ -+ /* Just do it */ -+ memcpy(&(threshold->low), &(spydata->spy_thr_low), -+ 2 * sizeof(struct iw_quality)); -+ -+ return 0; -+#else /* IW_WIRELESS_THRSPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_THRSPY */ -+} -+ -+#ifdef IW_WIRELESS_THRSPY -+/*------------------------------------------------------------------*/ -+/* -+ * Prepare and send a Spy Threshold event -+ */ -+static void iw_send_thrspy_event(struct net_device * dev, -+ struct iw_spy_data * spydata, -+ unsigned char * address, -+ struct iw_quality * wstats) -+{ -+ union iwreq_data wrqu; -+ struct iw_thrspy threshold; -+ -+ /* Init */ -+ wrqu.data.length = 1; -+ wrqu.data.flags = 0; -+ /* Copy address */ -+ memcpy(threshold.addr.sa_data, address, ETH_ALEN); -+ threshold.addr.sa_family = ARPHRD_ETHER; -+ /* Copy stats */ -+ memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); -+ /* Copy also thresholds */ -+ memcpy(&(threshold.low), &(spydata->spy_thr_low), -+ 2 * sizeof(struct iw_quality)); -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n", -+ threshold.addr.sa_data[0], -+ threshold.addr.sa_data[1], -+ threshold.addr.sa_data[2], -+ threshold.addr.sa_data[3], -+ threshold.addr.sa_data[4], -+ threshold.addr.sa_data[5], threshold.qual.level); -+#endif /* WE_SPY_DEBUG */ -+ -+ /* Send event to user space */ -+ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); -+} -+#endif /* IW_WIRELESS_THRSPY */ -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Call for the driver to update the spy data. -+ * For now, the spy data is a simple array. As the size of the array is -+ * small, this is good enough. If we wanted to support larger number of -+ * spy addresses, we should use something more efficient... -+ */ -+void wireless_spy_update(struct net_device * dev, -+ unsigned char * address, -+ struct iw_quality * wstats) -+{ -+#ifdef IW_WIRELESS_SPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ int i; -+ int match = -1; -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "wireless_spy_update() : offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]); -+#endif /* WE_SPY_DEBUG */ -+ -+ /* Update all records that match */ -+ for(i = 0; i < spydata->spy_number; i++) -+ if(!memcmp(address, spydata->spy_address[i], ETH_ALEN)) { -+ memcpy(&(spydata->spy_stat[i]), wstats, -+ sizeof(struct iw_quality)); -+ match = i; -+ } -+#ifdef IW_WIRELESS_THRSPY -+ /* Generate an event if we cross the spy threshold. -+ * To avoid event storms, we have a simple hysteresis : we generate -+ * event only when we go under the low threshold or above the -+ * high threshold. */ -+ if(match >= 0) { -+ if(spydata->spy_thr_under[match]) { -+ if(wstats->level > spydata->spy_thr_high.level) { -+ spydata->spy_thr_under[match] = 0; -+ iw_send_thrspy_event(dev, spydata, -+ address, wstats); -+ } -+ } else { -+ if(wstats->level < spydata->spy_thr_low.level) { -+ spydata->spy_thr_under[match] = 1; -+ iw_send_thrspy_event(dev, spydata, -+ address, wstats); -+ } -+ } -+ } -+#endif /* IW_WIRELESS_THRSPY */ -+#endif /* IW_WIRELESS_SPY */ - } -diff -u -p linux/net/netsyms.15.c linux/net/netsyms.c ---- linux/net/netsyms.15.c Fri Jan 10 16:56:32 2003 -+++ linux/net/netsyms.c Fri Jan 10 17:01:09 2003 -@@ -594,6 +594,11 @@ EXPORT_SYMBOL(softnet_data); - #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) - #include <net/iw_handler.h> - EXPORT_SYMBOL(wireless_send_event); -+EXPORT_SYMBOL(iw_handler_set_spy); -+EXPORT_SYMBOL(iw_handler_get_spy); -+EXPORT_SYMBOL(iw_handler_set_thrspy); -+EXPORT_SYMBOL(iw_handler_get_thrspy); -+EXPORT_SYMBOL(wireless_spy_update); - #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ - - #endif /* CONFIG_NET */ diff --git a/packages/linux/files/iw249_we16-6.diff b/packages/linux/files/iw249_we16-6.diff deleted file mode 100644 index 0a5aaab954..0000000000 --- a/packages/linux/files/iw249_we16-6.diff +++ /dev/null @@ -1,670 +0,0 @@ -diff -u -p linux/include/linux/wireless.15.h linux/include/linux/wireless.h ---- linux/include/linux/wireless.15.h 2004-11-05 14:59:33.000000000 -0800 -+++ linux/include/linux/wireless.h 2004-11-05 15:00:42.000000000 -0800 -@@ -1,7 +1,7 @@ - /* - * This file define a set of standard wireless extensions - * -- * Version : 15 12.7.02 -+ * Version : 16 2.4.03 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. -@@ -69,6 +69,8 @@ - - /***************************** INCLUDES *****************************/ - -+/* To minimise problems in user space, I might remove those headers -+ * at some point. Jean II */ - #include <linux/types.h> /* for "caddr_t" et al */ - #include <linux/socket.h> /* for "struct sockaddr" et al */ - #include <linux/if.h> /* for IFNAMSIZ and co... */ -@@ -80,7 +82,7 @@ - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ --#define WIRELESS_EXT 15 -+#define WIRELESS_EXT 16 - - /* - * Changes : -@@ -163,6 +165,16 @@ - * - Add IW_TXPOW_RANGE for range of Tx Powers - * - Add IWEVREGISTERED & IWEVEXPIRED events for Access Points - * - Add IW_MODE_MONITOR for passive monitor -+ * -+ * V15 to V16 -+ * ---------- -+ * - Increase the number of bitrates in iw_range to 32 (for 802.11g) -+ * - Increase the number of frequencies in iw_range to 32 (for 802.11b+a) -+ * - Reshuffle struct iw_range for increases, add filler -+ * - Increase IW_MAX_AP to 64 for driver returning a lot of addresses -+ * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support -+ * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" -+ * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index - */ - - /**************************** CONSTANTS ****************************/ -@@ -196,9 +208,11 @@ - /* SIOCGIWSTATS is strictly used between user space and the kernel, and - * is never passed to the driver (i.e. the driver will never see it). */ - --/* Mobile IP support (statistics per MAC address) */ -+/* Spy support (statistics per MAC address - used for Mobile IP support) */ - #define SIOCSIWSPY 0x8B10 /* set spy addresses */ - #define SIOCGIWSPY 0x8B11 /* get spy info (quality of link) */ -+#define SIOCSIWTHRSPY 0x8B12 /* set spy threshold (spy event) */ -+#define SIOCGIWTHRSPY 0x8B13 /* get spy threshold */ - - /* Access Point manipulation */ - #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ -@@ -294,7 +308,7 @@ - #define IW_PRIV_TYPE_FLOAT 0x5000 /* struct iw_freq */ - #define IW_PRIV_TYPE_ADDR 0x6000 /* struct sockaddr */ - --#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed nuber of args */ -+#define IW_PRIV_SIZE_FIXED 0x0800 /* Variable or fixed number of args */ - - #define IW_PRIV_SIZE_MASK 0x07FF /* Max number of those args */ - -@@ -306,13 +320,13 @@ - /* ----------------------- OTHER CONSTANTS ----------------------- */ - - /* Maximum frequencies in the range struct */ --#define IW_MAX_FREQUENCIES 16 -+#define IW_MAX_FREQUENCIES 32 - /* Note : if you have something like 80 frequencies, - * don't increase this constant and don't fill the frequency list. - * The user will be able to set by channel anyway... */ - - /* Maximum bit rates in the range struct */ --#define IW_MAX_BITRATES 8 -+#define IW_MAX_BITRATES 32 - - /* Maximum tx powers in the range struct */ - #define IW_MAX_TXPOWER 8 -@@ -320,8 +334,7 @@ - * a few of them in the struct iw_range. */ - - /* Maximum of address that you may set with SPY */ --#define IW_MAX_SPY 8 /* set */ --#define IW_MAX_GET_SPY 64 /* get */ -+#define IW_MAX_SPY 8 - - /* Maximum of address that you may get in the - list of access points in range */ -@@ -354,7 +367,8 @@ - #define IW_ENCODE_ENABLED 0x0000 /* Encoding enabled */ - #define IW_ENCODE_RESTRICTED 0x4000 /* Refuse non-encoded packets */ - #define IW_ENCODE_OPEN 0x2000 /* Accept non-encoded packets */ --#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ -+#define IW_ENCODE_NOKEY 0x0800 /* Key is write only, so not present */ -+#define IW_ENCODE_TEMP 0x0400 /* Temporary key */ - - /* Power management flags available (along with the value, if any) */ - #define IW_POWER_ON 0x0000 /* No details... */ -@@ -482,6 +496,17 @@ struct iw_missed - __u32 beacon; /* Missed beacons/superframe */ - }; - -+/* -+ * Quality range (for spy threshold) -+ */ -+struct iw_thrspy -+{ -+ struct sockaddr addr; /* Source address (hw/mac) */ -+ struct iw_quality qual; /* Quality of the link */ -+ struct iw_quality low; /* Low threshold */ -+ struct iw_quality high; /* High threshold */ -+}; -+ - /* ------------------------ WIRELESS STATS ------------------------ */ - /* - * Wireless statistics (used for /proc/net/wireless) -@@ -534,7 +559,7 @@ union iwreq_data - struct iw_quality qual; /* Quality part of statistics */ - - struct sockaddr ap_addr; /* Access point address */ -- struct sockaddr addr; /* Destination address (hw) */ -+ struct sockaddr addr; /* Destination address (hw/mac) */ - - struct iw_param param; /* Other small parameters */ - struct iw_point data; /* Other large parameters */ -@@ -582,17 +607,31 @@ struct iw_range - __u32 min_nwid; /* Minimal NWID we are able to set */ - __u32 max_nwid; /* Maximal NWID we are able to set */ - -- /* Frequency */ -- __u16 num_channels; /* Number of channels [0; num - 1] */ -- __u8 num_frequency; /* Number of entry in the list */ -- struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ -- /* Note : this frequency list doesn't need to fit channel numbers */ -+ /* Old Frequency (backward compat - moved lower ) */ -+ __u16 old_num_channels; -+ __u8 old_num_frequency; -+ /* Filler to keep "version" at the same offset */ -+ __s32 old_freq[6]; - - /* signal level threshold range */ - __s32 sensitivity; - - /* Quality of link & SNR stuff */ -+ /* Quality range (link, level, noise) -+ * If the quality is absolute, it will be in the range [0 ; max_qual], -+ * if the quality is dBm, it will be in the range [max_qual ; 0]. -+ * Don't forget that we use 8 bit arithmetics... */ - struct iw_quality max_qual; /* Quality of the link */ -+ /* This should contain the average/typical values of the quality -+ * indicator. This should be the threshold between a "good" and -+ * a "bad" link (example : monitor going from green to orange). -+ * Currently, user space apps like quality monitors don't have any -+ * way to calibrate the measurement. With this, they can split -+ * the range between 0 and max_qual in different quality level -+ * (using a geometric subdivision centered on the average). -+ * I expect that people doing the user space apps will feedback -+ * us on which value we need to put in each driver... */ -+ struct iw_quality avg_qual; /* Quality of the link */ - - /* Rates */ - __u8 num_bitrates; /* Number of entries in the list */ -@@ -619,6 +658,8 @@ struct iw_range - __u16 encoding_size[IW_MAX_ENCODING_SIZES]; /* Different token sizes */ - __u8 num_encoding_sizes; /* Number of entry in the list */ - __u8 max_encoding_tokens; /* Max number of tokens */ -+ /* For drivers that need a "login/passwd" form */ -+ __u8 encoding_login_index; /* token index for login token */ - - /* Transmit power */ - __u16 txpower_capa; /* What options are supported */ -@@ -638,18 +679,12 @@ struct iw_range - __s32 min_r_time; /* Minimal retry lifetime */ - __s32 max_r_time; /* Maximal retry lifetime */ - -- /* Average quality of link & SNR */ -- struct iw_quality avg_qual; /* Quality of the link */ -- /* This should contain the average/typical values of the quality -- * indicator. This should be the threshold between a "good" and -- * a "bad" link (example : monitor going from green to orange). -- * Currently, user space apps like quality monitors don't have any -- * way to calibrate the measurement. With this, they can split -- * the range between 0 and max_qual in different quality level -- * (using a geometric subdivision centered on the average). -- * I expect that people doing the user space apps will feedback -- * us on which value we need to put in each driver... -- */ -+ /* Frequency */ -+ __u16 num_channels; /* Number of channels [0; num - 1] */ -+ __u8 num_frequency; /* Number of entry in the list */ -+ struct iw_freq freq[IW_MAX_FREQUENCIES]; /* list */ -+ /* Note : this frequency list doesn't need to fit channel numbers, -+ * because each entry contain its channel index */ - }; - - /* -diff -u -p linux/include/net/iw_handler.15.h linux/include/net/iw_handler.h ---- linux/include/net/iw_handler.15.h 2004-11-05 14:59:47.000000000 -0800 -+++ linux/include/net/iw_handler.h 2004-11-05 15:00:42.000000000 -0800 -@@ -1,7 +1,7 @@ - /* - * This file define the new driver API for Wireless Extensions - * -- * Version : 4 21.6.02 -+ * Version : 5 4.12.02 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> - * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. -@@ -206,7 +206,7 @@ - * will be needed... - * I just plan to increment with each new version. - */ --#define IW_HANDLER_VERSION 4 -+#define IW_HANDLER_VERSION 5 - - /* - * Changes : -@@ -220,10 +220,18 @@ - * V3 to V4 - * -------- - * - Reshuffle IW_HEADER_TYPE_XXX to map IW_PRIV_TYPE_XXX changes -+ * -+ * V4 to V5 -+ * -------- -+ * - Add new spy support : struct iw_spy_data & prototypes - */ - - /**************************** CONSTANTS ****************************/ - -+/* Enable enhanced spy support. Disable to reduce footprint */ -+#define IW_WIRELESS_SPY -+#define IW_WIRELESS_THRSPY -+ - /* Special error message for the driver to indicate that we - * should do a commit after return from the iw_handler */ - #define EIWCOMMIT EINPROGRESS -@@ -315,6 +323,9 @@ struct iw_handler_def - * We will automatically export that to user space... */ - struct iw_priv_args * private_args; - -+ /* Driver enhanced spy support */ -+ long spy_offset; /* Spy data offset */ -+ - /* In the long term, get_wireless_stats will move from - * 'struct net_device' to here, to minimise bloat. */ - }; -@@ -350,6 +361,33 @@ struct iw_ioctl_description - - /* Need to think of short header translation table. Later. */ - -+/* --------------------- ENHANCED SPY SUPPORT --------------------- */ -+/* -+ * In the old days, the driver was handling spy support all by itself. -+ * Now, the driver can delegate this task to Wireless Extensions. -+ * It needs to include this struct in its private part and use the -+ * standard spy iw_handler. -+ */ -+ -+/* -+ * Instance specific spy data, i.e. addresses spied and quality for them. -+ */ -+struct iw_spy_data -+{ -+#ifdef IW_WIRELESS_SPY -+ /* --- Standard spy support --- */ -+ int spy_number; -+ u_char spy_address[IW_MAX_SPY][ETH_ALEN]; -+ struct iw_quality spy_stat[IW_MAX_SPY]; -+#ifdef IW_WIRELESS_THRSPY -+ /* --- Enhanced spy support (event) */ -+ struct iw_quality spy_thr_low; /* Low threshold */ -+ struct iw_quality spy_thr_high; /* High threshold */ -+ u_char spy_thr_under[IW_MAX_SPY]; -+#endif /* IW_WIRELESS_THRSPY */ -+#endif /* IW_WIRELESS_SPY */ -+}; -+ - /**************************** PROTOTYPES ****************************/ - /* - * Functions part of the Wireless Extensions (defined in net/core/wireless.c). -@@ -376,6 +414,31 @@ extern void wireless_send_event(struct n - /* We may need a function to send a stream of events to user space. - * More on that later... */ - -+/* Standard handler for SIOCSIWSPY */ -+extern int iw_handler_set_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Standard handler for SIOCGIWSPY */ -+extern int iw_handler_get_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Standard handler for SIOCSIWTHRSPY */ -+extern int iw_handler_set_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Standard handler for SIOCGIWTHRSPY */ -+extern int iw_handler_get_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra); -+/* Driver call to update spy records */ -+extern void wireless_spy_update(struct net_device * dev, -+ unsigned char * address, -+ struct iw_quality * wstats); -+ - /************************* INLINE FUNTIONS *************************/ - /* - * Function that are so simple that it's more efficient inlining them -diff -u -p linux/net/core/wireless.15.c linux/net/core/wireless.c ---- linux/net/core/wireless.15.c 2004-11-05 15:00:11.000000000 -0800 -+++ linux/net/core/wireless.c 2004-11-05 15:00:42.000000000 -0800 -@@ -2,7 +2,7 @@ - * This file implement the Wireless Extensions APIs. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ -@@ -43,6 +43,11 @@ - * o Turn on WE_STRICT_WRITE by default + kernel warning - * o Fix WE_STRICT_WRITE in ioctl_export_private() (32 => iw_num) - * o Fix off-by-one in test (extra_size <= IFNAMSIZ) -+ * -+ * v6 - 9.01.03 - Jean II -+ * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() -+ * o Add enhanced spy support : iw_handler_set_thrspy() and event. -+ * o Add WIRELESS_EXT version display in /proc/net/wireless - */ - - /***************************** INCLUDES *****************************/ -@@ -52,6 +57,7 @@ - #include <linux/types.h> /* off_t */ - #include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ - #include <linux/rtnetlink.h> /* rtnetlink stuff */ -+#include <linux/if_arp.h> /* ARPHRD_ETHER */ - - #include <linux/wireless.h> /* Pretty obvious */ - #include <net/iw_handler.h> /* New driver API */ -@@ -65,6 +71,7 @@ - /* Debuging stuff */ - #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ - #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ -+#undef WE_SPY_DEBUG /* Debug enhanced spy support */ - - /* Options */ - #define WE_EVENT_NETLINK /* Propagate events using rtnetlink */ -@@ -72,7 +79,7 @@ - - /************************* GLOBAL VARIABLES *************************/ - /* -- * You should not use global variables, because or re-entrancy. -+ * You should not use global variables, because of re-entrancy. - * On our case, it's only const, so it's OK... - */ - /* -@@ -115,11 +122,11 @@ static const struct iw_ioctl_description - /* SIOCSIWSPY */ - { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0}, - /* SIOCGIWSPY */ -- { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_GET_SPY, 0}, -- /* -- hole -- */ -- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -- /* -- hole -- */ -- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0}, -+ /* SIOCSIWTHRSPY */ -+ { IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0}, -+ /* SIOCGIWTHRSPY */ -+ { IW_HEADER_TYPE_POINT, 0, sizeof(struct iw_thrspy), 1, 1, 0}, - /* SIOCSIWAP */ - { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, - /* SIOCGIWAP */ -@@ -377,9 +384,9 @@ int dev_get_wireless_info(char * buffer, - struct net_device * dev; - - size = sprintf(buffer, -- "Inter-| sta-| Quality | Discarded packets | Missed\n" -- " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" -- ); -+ "Inter-| sta-| Quality | Discarded packets | Missed | WE\n" -+ " face | tus | link level noise | nwid crypt frag retry misc | beacon | %d\n", -+ WIRELESS_EXT); - - pos += size; - len += size; -@@ -1024,3 +1031,252 @@ void wireless_send_event(struct net_devi - - return; /* Always success, I guess ;-) */ - } -+ -+/********************** ENHANCED IWSPY SUPPORT **********************/ -+/* -+ * In the old days, the driver was handling spy support all by itself. -+ * Now, the driver can delegate this task to Wireless Extensions. -+ * It needs to use those standard spy iw_handler in struct iw_handler_def, -+ * push data to us via XXX and include struct iw_spy_data in its -+ * private part. -+ * One of the main advantage of centralising spy support here is that -+ * it becomes much easier to improve and extend it without having to touch -+ * the drivers. One example is the addition of the Spy-Threshold events. -+ * Note : IW_WIRELESS_SPY is defined in iw_handler.h -+ */ -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : set Spy List -+ */ -+int iw_handler_set_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_SPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct sockaddr * address = (struct sockaddr *) extra; -+ -+ /* Disable spy collection while we copy the addresses. -+ * As we don't disable interrupts, we need to do this to avoid races. -+ * As we are the only writer, this is good enough. */ -+ spydata->spy_number = 0; -+ -+ /* Are there are addresses to copy? */ -+ if(wrqu->data.length > 0) { -+ int i; -+ -+ /* Copy addresses */ -+ for(i = 0; i < wrqu->data.length; i++) -+ memcpy(spydata->spy_address[i], address[i].sa_data, -+ ETH_ALEN); -+ /* Reset stats */ -+ memset(spydata->spy_stat, 0, -+ sizeof(struct iw_quality) * IW_MAX_SPY); -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "iw_handler_set_spy() : offset %ld, spydata %p, num %d\n", dev->wireless_handlers->spy_offset, spydata, wrqu->data.length); -+ for (i = 0; i < wrqu->data.length; i++) -+ printk(KERN_DEBUG -+ "%02X:%02X:%02X:%02X:%02X:%02X \n", -+ spydata->spy_address[i][0], -+ spydata->spy_address[i][1], -+ spydata->spy_address[i][2], -+ spydata->spy_address[i][3], -+ spydata->spy_address[i][4], -+ spydata->spy_address[i][5]); -+#endif /* WE_SPY_DEBUG */ -+ } -+ /* Enable addresses */ -+ spydata->spy_number = wrqu->data.length; -+ -+ return 0; -+#else /* IW_WIRELESS_SPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_SPY */ -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : get Spy List -+ */ -+int iw_handler_get_spy(struct net_device * dev, -+ struct iw_request_info * info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_SPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct sockaddr * address = (struct sockaddr *) extra; -+ int i; -+ -+ wrqu->data.length = spydata->spy_number; -+ -+ /* Copy addresses. */ -+ for(i = 0; i < spydata->spy_number; i++) { -+ memcpy(address[i].sa_data, spydata->spy_address[i], ETH_ALEN); -+ address[i].sa_family = AF_UNIX; -+ } -+ /* Copy stats to the user buffer (just after). */ -+ if(spydata->spy_number > 0) -+ memcpy(extra + (sizeof(struct sockaddr) *spydata->spy_number), -+ spydata->spy_stat, -+ sizeof(struct iw_quality) * spydata->spy_number); -+ /* Reset updated flags. */ -+ for(i = 0; i < spydata->spy_number; i++) -+ spydata->spy_stat[i].updated = 0; -+ return 0; -+#else /* IW_WIRELESS_SPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_SPY */ -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : set spy threshold -+ */ -+int iw_handler_set_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_THRSPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra; -+ -+ /* Just do it */ -+ memcpy(&(spydata->spy_thr_low), &(threshold->low), -+ 2 * sizeof(struct iw_quality)); -+ -+ /* Clear flag */ -+ memset(spydata->spy_thr_under, '\0', sizeof(spydata->spy_thr_under)); -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "iw_handler_set_thrspy() : low %d ; high %d\n", spydata->spy_thr_low.level, spydata->spy_thr_high.level); -+#endif /* WE_SPY_DEBUG */ -+ -+ return 0; -+#else /* IW_WIRELESS_THRSPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_THRSPY */ -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Standard Wireless Handler : get spy threshold -+ */ -+int iw_handler_get_thrspy(struct net_device * dev, -+ struct iw_request_info *info, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+#ifdef IW_WIRELESS_THRSPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ struct iw_thrspy * threshold = (struct iw_thrspy *) extra; -+ -+ /* Just do it */ -+ memcpy(&(threshold->low), &(spydata->spy_thr_low), -+ 2 * sizeof(struct iw_quality)); -+ -+ return 0; -+#else /* IW_WIRELESS_THRSPY */ -+ return -EOPNOTSUPP; -+#endif /* IW_WIRELESS_THRSPY */ -+} -+ -+#ifdef IW_WIRELESS_THRSPY -+/*------------------------------------------------------------------*/ -+/* -+ * Prepare and send a Spy Threshold event -+ */ -+static void iw_send_thrspy_event(struct net_device * dev, -+ struct iw_spy_data * spydata, -+ unsigned char * address, -+ struct iw_quality * wstats) -+{ -+ union iwreq_data wrqu; -+ struct iw_thrspy threshold; -+ -+ /* Init */ -+ wrqu.data.length = 1; -+ wrqu.data.flags = 0; -+ /* Copy address */ -+ memcpy(threshold.addr.sa_data, address, ETH_ALEN); -+ threshold.addr.sa_family = ARPHRD_ETHER; -+ /* Copy stats */ -+ memcpy(&(threshold.qual), wstats, sizeof(struct iw_quality)); -+ /* Copy also thresholds */ -+ memcpy(&(threshold.low), &(spydata->spy_thr_low), -+ 2 * sizeof(struct iw_quality)); -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "iw_send_thrspy_event() : address %02X:%02X:%02X:%02X:%02X:%02X, level %d, up = %d\n", -+ threshold.addr.sa_data[0], -+ threshold.addr.sa_data[1], -+ threshold.addr.sa_data[2], -+ threshold.addr.sa_data[3], -+ threshold.addr.sa_data[4], -+ threshold.addr.sa_data[5], threshold.qual.level); -+#endif /* WE_SPY_DEBUG */ -+ -+ /* Send event to user space */ -+ wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); -+} -+#endif /* IW_WIRELESS_THRSPY */ -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Call for the driver to update the spy data. -+ * For now, the spy data is a simple array. As the size of the array is -+ * small, this is good enough. If we wanted to support larger number of -+ * spy addresses, we should use something more efficient... -+ */ -+void wireless_spy_update(struct net_device * dev, -+ unsigned char * address, -+ struct iw_quality * wstats) -+{ -+#ifdef IW_WIRELESS_SPY -+ struct iw_spy_data * spydata = (dev->priv + -+ dev->wireless_handlers->spy_offset); -+ int i; -+ int match = -1; -+ -+#ifdef WE_SPY_DEBUG -+ printk(KERN_DEBUG "wireless_spy_update() : offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]); -+#endif /* WE_SPY_DEBUG */ -+ -+ /* Update all records that match */ -+ for(i = 0; i < spydata->spy_number; i++) -+ if(!memcmp(address, spydata->spy_address[i], ETH_ALEN)) { -+ memcpy(&(spydata->spy_stat[i]), wstats, -+ sizeof(struct iw_quality)); -+ match = i; -+ } -+#ifdef IW_WIRELESS_THRSPY -+ /* Generate an event if we cross the spy threshold. -+ * To avoid event storms, we have a simple hysteresis : we generate -+ * event only when we go under the low threshold or above the -+ * high threshold. */ -+ if(match >= 0) { -+ if(spydata->spy_thr_under[match]) { -+ if(wstats->level > spydata->spy_thr_high.level) { -+ spydata->spy_thr_under[match] = 0; -+ iw_send_thrspy_event(dev, spydata, -+ address, wstats); -+ } -+ } else { -+ if(wstats->level < spydata->spy_thr_low.level) { -+ spydata->spy_thr_under[match] = 1; -+ iw_send_thrspy_event(dev, spydata, -+ address, wstats); -+ } -+ } -+ } -+#endif /* IW_WIRELESS_THRSPY */ -+#endif /* IW_WIRELESS_SPY */ -+} -diff -u -p linux/net/netsyms.15.c linux/net/netsyms.c ---- linux/net/netsyms.15.c 2004-11-05 15:00:25.000000000 -0800 -+++ linux/net/netsyms.c 2004-11-05 15:01:38.000000000 -0800 -@@ -589,9 +589,13 @@ EXPORT_SYMBOL(net_call_rx_atomic); - EXPORT_SYMBOL(softnet_data); - - #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) --/* Don't include the whole header mess for a single function */ --extern void wireless_send_event(struct net_device *dev, unsigned int cmd, union iwreq_data *wrqu, char *extra); -+#include <net/iw_handler.h> - EXPORT_SYMBOL(wireless_send_event); -+EXPORT_SYMBOL(iw_handler_set_spy); -+EXPORT_SYMBOL(iw_handler_get_spy); -+EXPORT_SYMBOL(iw_handler_set_thrspy); -+EXPORT_SYMBOL(iw_handler_get_thrspy); -+EXPORT_SYMBOL(wireless_spy_update); - #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ - - #endif /* CONFIG_NET */ diff --git a/packages/linux/files/iw249_we17-13.diff b/packages/linux/files/iw249_we17-13.diff deleted file mode 100644 index 674f4ffbc0..0000000000 --- a/packages/linux/files/iw249_we17-13.diff +++ /dev/null @@ -1,768 +0,0 @@ -diff -u -p linux/include/linux/netdevice.we16.h linux/include/linux/netdevice.h ---- linux/include/linux/netdevice.we16.h 2005-02-03 14:54:56.000000000 -0800 -+++ linux/include/linux/netdevice.h 2005-02-03 15:43:30.000000000 -0800 -@@ -295,7 +295,9 @@ struct net_device - - /* List of functions to handle Wireless Extensions (instead of ioctl). - * See <net/iw_handler.h> for details. Jean II */ -- struct iw_handler_def * wireless_handlers; -+ const struct iw_handler_def * wireless_handlers; -+ /* Instance data managed by the core of Wireless Extensions. */ -+ struct iw_public_data * wireless_data; - - struct ethtool_ops *ethtool_ops; - -diff -u -p linux/include/linux/wireless.we16.h linux/include/linux/wireless.h ---- linux/include/linux/wireless.we16.h 2005-02-03 14:55:04.000000000 -0800 -+++ linux/include/linux/wireless.h 2005-02-03 15:44:48.000000000 -0800 -@@ -1,10 +1,10 @@ - /* - * This file define a set of standard wireless extensions - * -- * Version : 16 2.4.03 -+ * Version : 17 21.6.04 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. - */ - - #ifndef _LINUX_WIRELESS_H -@@ -47,12 +47,12 @@ - * # include/net/iw_handler.h - * - * Note as well that /proc/net/wireless implementation has now moved in : -- * # include/linux/wireless.c -+ * # net/core/wireless.c - * - * Wireless Events (2002 -> onward) : - * -------------------------------- - * Events are defined at the end of this file, and implemented in : -- * # include/linux/wireless.c -+ * # net/core/wireless.c - * - * Other comments : - * -------------- -@@ -82,7 +82,7 @@ - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ --#define WIRELESS_EXT 16 -+#define WIRELESS_EXT 17 - - /* - * Changes : -@@ -175,6 +175,13 @@ - * - Remove IW_MAX_GET_SPY because conflict with enhanced spy support - * - Add SIOCSIWTHRSPY/SIOCGIWTHRSPY and "struct iw_thrspy" - * - Add IW_ENCODE_TEMP and iw_range->encoding_login_index -+ * -+ * V16 to V17 -+ * ---------- -+ * - Add flags to frequency -> auto/fixed -+ * - Document (struct iw_quality *)->updated, add new flags (INVALID) -+ * - Wireless Event capability in struct iw_range -+ * - Add support for relative TxPower (yick !) - */ - - /**************************** CONSTANTS ****************************/ -@@ -251,7 +258,7 @@ - - /* -------------------- DEV PRIVATE IOCTL LIST -------------------- */ - --/* These 16 ioctl are wireless device private. -+/* These 32 ioctl are wireless device private, for 16 commands. - * Each driver is free to use them for whatever purpose it chooses, - * however the driver *must* export the description of those ioctls - * with SIOCGIWPRIV and *must* use arguments as defined below. -@@ -266,8 +273,8 @@ - * We now have 32 commands, so a bit more space ;-). - * Also, all 'odd' commands are only usable by root and don't return the - * content of ifr/iwr to user (but you are not obliged to use the set/get -- * convention, just use every other two command). -- * And I repeat : you are not obliged to use them with iwspy, but you -+ * convention, just use every other two command). More details in iwpriv.c. -+ * And I repeat : you are not forced to use them with iwpriv, but you - * must be compliant with it. - */ - -@@ -352,6 +359,18 @@ - #define IW_MODE_SECOND 5 /* Secondary master/repeater (backup) */ - #define IW_MODE_MONITOR 6 /* Passive monitor (listen only) */ - -+/* Statistics flags (bitmask in updated) */ -+#define IW_QUAL_QUAL_UPDATED 0x1 /* Value was updated since last read */ -+#define IW_QUAL_LEVEL_UPDATED 0x2 -+#define IW_QUAL_NOISE_UPDATED 0x4 -+#define IW_QUAL_QUAL_INVALID 0x10 /* Driver doesn't provide value */ -+#define IW_QUAL_LEVEL_INVALID 0x20 -+#define IW_QUAL_NOISE_INVALID 0x40 -+ -+/* Frequency flags */ -+#define IW_FREQ_AUTO 0x00 /* Let the driver decides */ -+#define IW_FREQ_FIXED 0x01 /* Force a specific value */ -+ - /* Maximum number of size of encoding token available - * they are listed in the range structure */ - #define IW_MAX_ENCODING_SIZES 8 -@@ -390,6 +409,7 @@ - #define IW_TXPOW_TYPE 0x00FF /* Type of value */ - #define IW_TXPOW_DBM 0x0000 /* Value is in dBm */ - #define IW_TXPOW_MWATT 0x0001 /* Value is in mW */ -+#define IW_TXPOW_RELATIVE 0x0002 /* Value is in arbitrary units */ - #define IW_TXPOW_RANGE 0x1000 /* Range of value between min/max */ - - /* Retry limits and lifetime flags available */ -@@ -418,6 +438,25 @@ - /* Max number of char in custom event - use multiple of them if needed */ - #define IW_CUSTOM_MAX 256 /* In bytes */ - -+/* Event capability macros - in (struct iw_range *)->event_capa -+ * Because we have more than 32 possible events, we use an array of -+ * 32 bit bitmasks. Note : 32 bits = 0x20 = 2^5. */ -+#define IW_EVENT_CAPA_BASE(cmd) ((cmd >= SIOCIWFIRSTPRIV) ? \ -+ (cmd - SIOCIWFIRSTPRIV + 0x60) : \ -+ (cmd - SIOCSIWCOMMIT)) -+#define IW_EVENT_CAPA_INDEX(cmd) (IW_EVENT_CAPA_BASE(cmd) >> 5) -+#define IW_EVENT_CAPA_MASK(cmd) (1 << (IW_EVENT_CAPA_BASE(cmd) & 0x1F)) -+/* Event capability constants - event autogenerated by the kernel -+ * This list is valid for most 802.11 devices, customise as needed... */ -+#define IW_EVENT_CAPA_K_0 (IW_EVENT_CAPA_MASK(0x8B04) | \ -+ IW_EVENT_CAPA_MASK(0x8B06) | \ -+ IW_EVENT_CAPA_MASK(0x8B1A)) -+#define IW_EVENT_CAPA_K_1 (IW_EVENT_CAPA_MASK(0x8B2A)) -+/* "Easy" macro to set events in iw_range (less efficient) */ -+#define IW_EVENT_CAPA_SET(event_capa, cmd) (event_capa[IW_EVENT_CAPA_INDEX(cmd)] |= IW_EVENT_CAPA_MASK(cmd)) -+#define IW_EVENT_CAPA_SET_KERNEL(event_capa) {event_capa[0] |= IW_EVENT_CAPA_K_0; event_capa[1] |= IW_EVENT_CAPA_K_1; } -+ -+ - /****************************** TYPES ******************************/ - - /* --------------------------- SUBTYPES --------------------------- */ -@@ -456,7 +495,7 @@ struct iw_freq - __s32 m; /* Mantissa */ - __s16 e; /* Exponent */ - __u8 i; /* List index (when in range struct) */ -- __u8 pad; /* Unused - just for alignement */ -+ __u8 flags; /* Flags (fixed/auto) */ - }; - - /* -@@ -610,11 +649,12 @@ struct iw_range - /* Old Frequency (backward compat - moved lower ) */ - __u16 old_num_channels; - __u8 old_num_frequency; -- /* Filler to keep "version" at the same offset */ -- __s32 old_freq[6]; -+ -+ /* Wireless event capability bitmasks */ -+ __u32 event_capa[6]; - - /* signal level threshold range */ -- __s32 sensitivity; -+ __s32 sensitivity; - - /* Quality of link & SNR stuff */ - /* Quality range (link, level, noise) -diff -u -p linux/include/net/iw_handler.we16.h linux/include/net/iw_handler.h ---- linux/include/net/iw_handler.we16.h 2005-02-03 14:55:26.000000000 -0800 -+++ linux/include/net/iw_handler.h 2005-02-03 15:47:04.000000000 -0800 -@@ -1,10 +1,10 @@ - /* - * This file define the new driver API for Wireless Extensions - * -- * Version : 5 4.12.02 -+ * Version : 6 21.6.04 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 2001-2004 Jean Tourrilhes, All Rights Reserved. - */ - - #ifndef _IW_HANDLER_H -@@ -206,7 +206,7 @@ - * will be needed... - * I just plan to increment with each new version. - */ --#define IW_HANDLER_VERSION 5 -+#define IW_HANDLER_VERSION 6 - - /* - * Changes : -@@ -224,11 +224,18 @@ - * V4 to V5 - * -------- - * - Add new spy support : struct iw_spy_data & prototypes -+ * -+ * V5 to V6 -+ * -------- -+ * - Change the way we get to spy_data method for added safety -+ * - Remove spy #ifdef, they are always on -> cleaner code -+ * - Add IW_DESCR_FLAG_NOMAX flag for very large requests -+ * - Start migrating get_wireless_stats to struct iw_handler_def - */ - - /**************************** CONSTANTS ****************************/ - --/* Enable enhanced spy support. Disable to reduce footprint */ -+/* Enhanced spy support available */ - #define IW_WIRELESS_SPY - #define IW_WIRELESS_THRSPY - -@@ -258,6 +265,7 @@ - #define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ - #define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */ - /* SET : Omit payload from generated iwevent */ -+#define IW_DESCR_FLAG_NOMAX 0x0008 /* GET : no limit on request size */ - /* Driver level flags */ - #define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ - -@@ -311,23 +319,25 @@ struct iw_handler_def - /* Array of handlers for standard ioctls - * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME] - */ -- iw_handler * standard; -+ const iw_handler * standard; - - /* Array of handlers for private ioctls - * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV] - */ -- iw_handler * private; -+ const iw_handler * private; - - /* Arguments of private handler. This one is just a list, so you - * can put it in any order you want and should not leave holes... - * We will automatically export that to user space... */ -- struct iw_priv_args * private_args; -+ const struct iw_priv_args * private_args; - -- /* Driver enhanced spy support */ -- long spy_offset; /* Spy data offset */ -+ /* This field will be *removed* in the next version of WE */ -+ long spy_offset; /* DO NOT USE */ - -- /* In the long term, get_wireless_stats will move from -- * 'struct net_device' to here, to minimise bloat. */ -+ /* New location of get_wireless_stats, to de-bloat struct net_device. -+ * The old pointer in struct net_device will be gradually phased -+ * out, and drivers are encouraged to use this one... */ -+ struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); - }; - - /* ---------------------- IOCTL DESCRIPTION ---------------------- */ -@@ -374,18 +384,29 @@ struct iw_ioctl_description - */ - struct iw_spy_data - { --#ifdef IW_WIRELESS_SPY - /* --- Standard spy support --- */ - int spy_number; - u_char spy_address[IW_MAX_SPY][ETH_ALEN]; - struct iw_quality spy_stat[IW_MAX_SPY]; --#ifdef IW_WIRELESS_THRSPY - /* --- Enhanced spy support (event) */ - struct iw_quality spy_thr_low; /* Low threshold */ - struct iw_quality spy_thr_high; /* High threshold */ - u_char spy_thr_under[IW_MAX_SPY]; --#endif /* IW_WIRELESS_THRSPY */ --#endif /* IW_WIRELESS_SPY */ -+}; -+ -+/* --------------------- DEVICE WIRELESS DATA --------------------- */ -+/* -+ * This is all the wireless data specific to a device instance that -+ * is managed by the core of Wireless Extensions. -+ * We only keep pointer to those structures, so that a driver is free -+ * to share them between instances. -+ * This structure should be initialised before registering the device. -+ * Access to this data follow the same rules as any other struct net_device -+ * data (i.e. valid as long as struct net_device exist, same locking rules). -+ */ -+struct iw_public_data { -+ /* Driver enhanced spy support */ -+ struct iw_spy_data * spy_data; - }; - - /**************************** PROTOTYPES ****************************/ -diff -u -p linux/net/core/dev.we16.c linux/net/core/dev.c ---- linux/net/core/dev.we16.c 2005-02-03 14:55:56.000000000 -0800 -+++ linux/net/core/dev.c 2005-02-03 15:28:48.000000000 -0800 -@@ -2426,7 +2426,7 @@ int dev_ioctl(unsigned int cmd, void *ar - /* Follow me in net/core/wireless.c */ - ret = wireless_process_ioctl(&ifr, cmd); - rtnl_unlock(); -- if (!ret && IW_IS_GET(cmd) && -+ if (IW_IS_GET(cmd) && - copy_to_user(arg, &ifr, sizeof(struct ifreq))) - return -EFAULT; - return ret; -diff -u -p linux/net/core/wireless.we16.c linux/net/core/wireless.c ---- linux/net/core/wireless.we16.c 2005-02-03 14:56:09.000000000 -0800 -+++ linux/net/core/wireless.c 2005-02-03 16:33:22.000000000 -0800 -@@ -2,7 +2,7 @@ - * This file implement the Wireless Extensions APIs. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2003 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2004 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ -@@ -48,6 +48,16 @@ - * o Add common spy support : iw_handler_set_spy(), wireless_spy_update() - * o Add enhanced spy support : iw_handler_set_thrspy() and event. - * o Add WIRELESS_EXT version display in /proc/net/wireless -+ * -+ * v6 - 18.06.04 - Jean II -+ * o Change get_spydata() method for added safety -+ * o Remove spy #ifdef, they are always on -> cleaner code -+ * o Allow any size GET request if user specifies length > max -+ * and if request has IW_DESCR_FLAG_NOMAX flag or is SIOCGIWPRIV -+ * o Start migrating get_wireless_stats to struct iw_handler_def -+ * o Add wmb() in iw_handler_set_spy() for non-coherent archs/cpus -+ * Based on patch from Pavel Roskin <proski@gnu.org> : -+ * o Fix kernel data leak to user space in private handler handling - */ - - /***************************** INCLUDES *****************************/ -@@ -64,11 +74,7 @@ - - /**************************** CONSTANTS ****************************/ - --/* Enough lenience, let's make sure things are proper... */ --#define WE_STRICT_WRITE /* Check write buffer size */ --/* I'll probably drop both the define and kernel message in the next version */ -- --/* Debuging stuff */ -+/* Debugging stuff */ - #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ - #undef WE_EVENT_DEBUG /* Debug Event dispatcher */ - #undef WE_SPY_DEBUG /* Debug enhanced spy support */ -@@ -134,11 +140,11 @@ static const struct iw_ioctl_description - /* -- hole -- */ - { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, - /* SIOCGIWAPLIST */ -- { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0}, -+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, IW_DESCR_FLAG_NOMAX}, - /* SIOCSIWSCAN */ - { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, - /* SIOCGIWSCAN */ -- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, 0}, -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, IW_DESCR_FLAG_NOMAX}, - /* SIOCSIWESSID */ - { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_EVENT}, - /* SIOCGIWESSID */ -@@ -203,7 +209,7 @@ static const int standard_event_num = (s - sizeof(struct iw_ioctl_description)); - - /* Size (in bytes) of the various private data types */ --static const char priv_type_size[] = { -+static const char iw_priv_type_size[] = { - 0, /* IW_PRIV_TYPE_NONE */ - 1, /* IW_PRIV_TYPE_BYTE */ - 1, /* IW_PRIV_TYPE_CHAR */ -@@ -270,12 +276,15 @@ static inline iw_handler get_handler(str - */ - static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) - { -+ /* New location */ -+ if((dev->wireless_handlers != NULL) && -+ (dev->wireless_handlers->get_wireless_stats != NULL)) -+ return dev->wireless_handlers->get_wireless_stats(dev); -+ -+ /* Old location, will be phased out in next WE */ - return (dev->get_wireless_stats ? - dev->get_wireless_stats(dev) : - (struct iw_statistics *) NULL); -- /* In the future, get_wireless_stats may move from 'struct net_device' -- * to 'struct iw_handler_def', to de-bloat struct net_device. -- * Definitely worse a thought... */ - } - - /* ---------------------------------------------------------------- */ -@@ -310,14 +319,32 @@ static inline int call_commit_handler(st - - /* ---------------------------------------------------------------- */ - /* -- * Number of private arguments -+ * Calculate size of private arguments - */ - static inline int get_priv_size(__u16 args) - { - int num = args & IW_PRIV_SIZE_MASK; - int type = (args & IW_PRIV_TYPE_MASK) >> 12; - -- return num * priv_type_size[type]; -+ return num * iw_priv_type_size[type]; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Re-calculate the size of private arguments -+ */ -+static inline int adjust_priv_size(__u16 args, -+ union iwreq_data * wrqu) -+{ -+ int num = wrqu->data.length; -+ int max = args & IW_PRIV_SIZE_MASK; -+ int type = (args & IW_PRIV_TYPE_MASK) >> 12; -+ -+ /* Make sure the driver doesn't goof up */ -+ if (max < num) -+ num = max; -+ -+ return num * iw_priv_type_size[type]; - } - - -@@ -350,11 +377,14 @@ static inline int sprintf_wireless_stats - dev->name, - stats->status, - stats->qual.qual, -- stats->qual.updated & 1 ? '.' : ' ', -+ stats->qual.updated & IW_QUAL_QUAL_UPDATED -+ ? '.' : ' ', - ((__u8) stats->qual.level), -- stats->qual.updated & 2 ? '.' : ' ', -+ stats->qual.updated & IW_QUAL_LEVEL_UPDATED -+ ? '.' : ' ', - ((__u8) stats->qual.noise), -- stats->qual.updated & 4 ? '.' : ' ', -+ stats->qual.updated & IW_QUAL_NOISE_UPDATED -+ ? '.' : ' ', - stats->discard.nwid, - stats->discard.code, - stats->discard.fragment, -@@ -470,13 +500,15 @@ static inline int ioctl_export_private(s - /* Check NULL pointer */ - if(iwr->u.data.pointer == NULL) - return -EFAULT; --#ifdef WE_STRICT_WRITE -+ - /* Check if there is enough buffer up there */ - if(iwr->u.data.length < dev->wireless_handlers->num_private_args) { -- printk(KERN_ERR "%s (WE) : Buffer for request SIOCGIWPRIV too small (%d<%d)\n", dev->name, iwr->u.data.length, dev->wireless_handlers->num_private_args); -+ /* User space can't know in advance how large the buffer -+ * needs to be. Give it a hint, so that we can support -+ * any size buffer we want somewhat efficiently... */ -+ iwr->u.data.length = dev->wireless_handlers->num_private_args; - return -E2BIG; - } --#endif /* WE_STRICT_WRITE */ - - /* Set the number of available ioctls. */ - iwr->u.data.length = dev->wireless_handlers->num_private_args; -@@ -505,7 +537,6 @@ static inline int ioctl_standard_call(st - const struct iw_ioctl_description * descr; - struct iw_request_info info; - int ret = -EINVAL; -- int user_size = 0; - - /* Get the description of the IOCTL */ - if((cmd - SIOCIWFIRST) >= standard_ioctl_num) -@@ -536,8 +567,14 @@ static inline int ioctl_standard_call(st - #endif /* WE_SET_EVENT */ - } else { - char * extra; -+ int extra_size; -+ int user_length = 0; - int err; - -+ /* Calculate space needed by arguments. Always allocate -+ * for max space. Easier, and won't last long... */ -+ extra_size = descr->max_tokens * descr->token_size; -+ - /* Check what user space is giving us */ - if(IW_IS_SET(cmd)) { - /* Check NULL pointer */ -@@ -554,18 +591,33 @@ static inline int ioctl_standard_call(st - if(iwr->u.data.pointer == NULL) - return -EFAULT; - /* Save user space buffer size for checking */ -- user_size = iwr->u.data.length; -+ user_length = iwr->u.data.length; -+ -+ /* Don't check if user_length > max to allow forward -+ * compatibility. The test user_length < min is -+ * implied by the test at the end. */ -+ -+ /* Support for very large requests */ -+ if((descr->flags & IW_DESCR_FLAG_NOMAX) && -+ (user_length > descr->max_tokens)) { -+ /* Allow userspace to GET more than max so -+ * we can support any size GET requests. -+ * There is still a limit : -ENOMEM. */ -+ extra_size = user_length * descr->token_size; -+ /* Note : user_length is originally a __u16, -+ * and token_size is controlled by us, -+ * so extra_size won't get negative and -+ * won't overflow... */ -+ } - } - - #ifdef WE_IOCTL_DEBUG - printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", -- dev->name, descr->max_tokens * descr->token_size); -+ dev->name, extra_size); - #endif /* WE_IOCTL_DEBUG */ - -- /* Always allocate for max space. Easier, and won't last -- * long... */ -- extra = kmalloc(descr->max_tokens * descr->token_size, -- GFP_KERNEL); -+ /* Create the kernel buffer */ -+ extra = kmalloc(extra_size, GFP_KERNEL); - if (extra == NULL) { - return -ENOMEM; - } -@@ -591,14 +643,11 @@ static inline int ioctl_standard_call(st - - /* If we have something to return to the user */ - if (!ret && IW_IS_GET(cmd)) { --#ifdef WE_STRICT_WRITE - /* Check if there is enough buffer up there */ -- if(user_size < iwr->u.data.length) { -- printk(KERN_ERR "%s (WE) : Buffer for request %04X too small (%d<%d)\n", dev->name, cmd, user_size, iwr->u.data.length); -+ if(user_length < iwr->u.data.length) { - kfree(extra); - return -E2BIG; - } --#endif /* WE_STRICT_WRITE */ - - err = copy_to_user(iwr->u.data.pointer, extra, - iwr->u.data.length * -@@ -661,7 +710,7 @@ static inline int ioctl_private_call(str - iw_handler handler) - { - struct iwreq * iwr = (struct iwreq *) ifr; -- struct iw_priv_args * descr = NULL; -+ const struct iw_priv_args * descr = NULL; - struct iw_request_info info; - int extra_size = 0; - int i; -@@ -701,7 +750,7 @@ static inline int ioctl_private_call(str - ((extra_size + offset) <= IFNAMSIZ)) - extra_size = 0; - } else { -- /* Size of set arguments */ -+ /* Size of get arguments */ - extra_size = get_priv_size(descr->get_args); - - /* Does it fits in iwr ? */ -@@ -771,6 +820,14 @@ static inline int ioctl_private_call(str - - /* If we have something to return to the user */ - if (!ret && IW_IS_GET(cmd)) { -+ -+ /* Adjust for the actual length if it's variable, -+ * avoid leaking kernel bits outside. */ -+ if (!(descr->get_args & IW_PRIV_SIZE_FIXED)) { -+ extra_size = adjust_priv_size(descr->get_args, -+ &(iwr->u)); -+ } -+ - err = copy_to_user(iwr->u.data.pointer, extra, - extra_size); - if (err) -@@ -1042,9 +1099,25 @@ void wireless_send_event(struct net_devi - * One of the main advantage of centralising spy support here is that - * it becomes much easier to improve and extend it without having to touch - * the drivers. One example is the addition of the Spy-Threshold events. -- * Note : IW_WIRELESS_SPY is defined in iw_handler.h - */ - -+/* ---------------------------------------------------------------- */ -+/* -+ * Return the pointer to the spy data in the driver. -+ * Because this is called on the Rx path via wireless_spy_update(), -+ * we want it to be efficient... -+ */ -+static inline struct iw_spy_data * get_spydata(struct net_device *dev) -+{ -+ /* This is the new way */ -+ if(dev->wireless_data) -+ return(dev->wireless_data->spy_data); -+ -+ /* This is the old way. Doesn't work for multi-headed drivers. -+ * It will be removed in the next version of WE. */ -+ return (dev->priv + dev->wireless_handlers->spy_offset); -+} -+ - /*------------------------------------------------------------------*/ - /* - * Standard Wireless Handler : set Spy List -@@ -1054,16 +1127,26 @@ int iw_handler_set_spy(struct net_device - union iwreq_data * wrqu, - char * extra) - { --#ifdef IW_WIRELESS_SPY -- struct iw_spy_data * spydata = (dev->priv + -- dev->wireless_handlers->spy_offset); -+ struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - -+ /* Make sure driver is not buggy or using the old API */ -+ if(!spydata) -+ return -EOPNOTSUPP; -+ - /* Disable spy collection while we copy the addresses. -- * As we don't disable interrupts, we need to do this to avoid races. -- * As we are the only writer, this is good enough. */ -+ * While we copy addresses, any call to wireless_spy_update() -+ * will NOP. This is OK, as anyway the addresses are changing. */ - spydata->spy_number = 0; - -+ /* We want to operate without locking, because wireless_spy_update() -+ * most likely will happen in the interrupt handler, and therefore -+ * have its own locking constraints and needs performance. -+ * The rtnl_lock() make sure we don't race with the other iw_handlers. -+ * This make sure wireless_spy_update() "see" that the spy list -+ * is temporarily disabled. */ -+ wmb(); -+ - /* Are there are addresses to copy? */ - if(wrqu->data.length > 0) { - int i; -@@ -1089,13 +1172,14 @@ int iw_handler_set_spy(struct net_device - spydata->spy_address[i][5]); - #endif /* WE_SPY_DEBUG */ - } -+ -+ /* Make sure above is updated before re-enabling */ -+ wmb(); -+ - /* Enable addresses */ - spydata->spy_number = wrqu->data.length; - - return 0; --#else /* IW_WIRELESS_SPY */ -- return -EOPNOTSUPP; --#endif /* IW_WIRELESS_SPY */ - } - - /*------------------------------------------------------------------*/ -@@ -1107,12 +1191,14 @@ int iw_handler_get_spy(struct net_device - union iwreq_data * wrqu, - char * extra) - { --#ifdef IW_WIRELESS_SPY -- struct iw_spy_data * spydata = (dev->priv + -- dev->wireless_handlers->spy_offset); -+ struct iw_spy_data * spydata = get_spydata(dev); - struct sockaddr * address = (struct sockaddr *) extra; - int i; - -+ /* Make sure driver is not buggy or using the old API */ -+ if(!spydata) -+ return -EOPNOTSUPP; -+ - wrqu->data.length = spydata->spy_number; - - /* Copy addresses. */ -@@ -1129,9 +1215,6 @@ int iw_handler_get_spy(struct net_device - for(i = 0; i < spydata->spy_number; i++) - spydata->spy_stat[i].updated = 0; - return 0; --#else /* IW_WIRELESS_SPY */ -- return -EOPNOTSUPP; --#endif /* IW_WIRELESS_SPY */ - } - - /*------------------------------------------------------------------*/ -@@ -1143,11 +1226,13 @@ int iw_handler_set_thrspy(struct net_dev - union iwreq_data * wrqu, - char * extra) - { --#ifdef IW_WIRELESS_THRSPY -- struct iw_spy_data * spydata = (dev->priv + -- dev->wireless_handlers->spy_offset); -+ struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - -+ /* Make sure driver is not buggy or using the old API */ -+ if(!spydata) -+ return -EOPNOTSUPP; -+ - /* Just do it */ - memcpy(&(spydata->spy_thr_low), &(threshold->low), - 2 * sizeof(struct iw_quality)); -@@ -1160,9 +1245,6 @@ int iw_handler_set_thrspy(struct net_dev - #endif /* WE_SPY_DEBUG */ - - return 0; --#else /* IW_WIRELESS_THRSPY */ -- return -EOPNOTSUPP; --#endif /* IW_WIRELESS_THRSPY */ - } - - /*------------------------------------------------------------------*/ -@@ -1174,22 +1256,20 @@ int iw_handler_get_thrspy(struct net_dev - union iwreq_data * wrqu, - char * extra) - { --#ifdef IW_WIRELESS_THRSPY -- struct iw_spy_data * spydata = (dev->priv + -- dev->wireless_handlers->spy_offset); -+ struct iw_spy_data * spydata = get_spydata(dev); - struct iw_thrspy * threshold = (struct iw_thrspy *) extra; - -+ /* Make sure driver is not buggy or using the old API */ -+ if(!spydata) -+ return -EOPNOTSUPP; -+ - /* Just do it */ - memcpy(&(threshold->low), &(spydata->spy_thr_low), - 2 * sizeof(struct iw_quality)); - - return 0; --#else /* IW_WIRELESS_THRSPY */ -- return -EOPNOTSUPP; --#endif /* IW_WIRELESS_THRSPY */ - } - --#ifdef IW_WIRELESS_THRSPY - /*------------------------------------------------------------------*/ - /* - * Prepare and send a Spy Threshold event -@@ -1227,7 +1307,6 @@ static void iw_send_thrspy_event(struct - /* Send event to user space */ - wireless_send_event(dev, SIOCGIWTHRSPY, &wrqu, (char *) &threshold); - } --#endif /* IW_WIRELESS_THRSPY */ - - /* ---------------------------------------------------------------- */ - /* -@@ -1240,12 +1319,14 @@ void wireless_spy_update(struct net_devi - unsigned char * address, - struct iw_quality * wstats) - { --#ifdef IW_WIRELESS_SPY -- struct iw_spy_data * spydata = (dev->priv + -- dev->wireless_handlers->spy_offset); -+ struct iw_spy_data * spydata = get_spydata(dev); - int i; - int match = -1; - -+ /* Make sure driver is not buggy or using the old API */ -+ if(!spydata) -+ return; -+ - #ifdef WE_SPY_DEBUG - printk(KERN_DEBUG "wireless_spy_update() : offset %ld, spydata %p, address %02X:%02X:%02X:%02X:%02X:%02X\n", dev->wireless_handlers->spy_offset, spydata, address[0], address[1], address[2], address[3], address[4], address[5]); - #endif /* WE_SPY_DEBUG */ -@@ -1257,7 +1338,7 @@ void wireless_spy_update(struct net_devi - sizeof(struct iw_quality)); - match = i; - } --#ifdef IW_WIRELESS_THRSPY -+ - /* Generate an event if we cross the spy threshold. - * To avoid event storms, we have a simple hysteresis : we generate - * event only when we go under the low threshold or above the -@@ -1277,6 +1358,4 @@ void wireless_spy_update(struct net_devi - } - } - } --#endif /* IW_WIRELESS_THRSPY */ --#endif /* IW_WIRELESS_SPY */ - } diff --git a/packages/linux/files/iw_handlers.w13-5.diff b/packages/linux/files/iw_handlers.w13-5.diff deleted file mode 100644 index a27a7654a9..0000000000 --- a/packages/linux/files/iw_handlers.w13-5.diff +++ /dev/null @@ -1,1513 +0,0 @@ -diff -u -p -r --new-file linux/include/linux-w12/netdevice.h linux/include/linux/netdevice.h ---- linux/include/linux-w12/netdevice.h Thu Nov 22 11:47:09 2001 -+++ linux/include/linux/netdevice.h Thu Jan 17 12:00:39 2002 -@@ -278,6 +278,10 @@ struct net_device - struct net_device_stats* (*get_stats)(struct net_device *dev); - struct iw_statistics* (*get_wireless_stats)(struct net_device *dev); - -+ /* List of functions to handle Wireless Extensions (instead of ioctl). -+ * See <net/iw_handler.h> for details. Jean II */ -+ struct iw_handler_def * wireless_handlers; -+ - /* - * This marks the end of the "visible" part of the structure. All - * fields hereafter are internal to the system, and may change at -diff -u -p -r --new-file linux/include/linux-w12/wireless.h linux/include/linux/wireless.h ---- linux/include/linux-w12/wireless.h Thu Nov 22 11:47:12 2001 -+++ linux/include/linux/wireless.h Thu Jan 17 12:04:08 2002 -@@ -1,9 +1,10 @@ - /* - * This file define a set of standard wireless extensions - * -- * Version : 12 5.10.01 -+ * Version : 13 6.12.01 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -+ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. - */ - - #ifndef _LINUX_WIRELESS_H -@@ -11,6 +12,8 @@ - - /************************** DOCUMENTATION **************************/ - /* -+ * Initial APIs (1996 -> onward) : -+ * ----------------------------- - * Basically, the wireless extensions are for now a set of standard ioctl - * call + /proc/net/wireless - * -@@ -27,16 +30,27 @@ - * We have the list of command plus a structure descibing the - * data exchanged... - * Note that to add these ioctl, I was obliged to modify : -- * net/core/dev.c (two place + add include) -- * net/ipv4/af_inet.c (one place + add include) -+ * # net/core/dev.c (two place + add include) -+ * # net/ipv4/af_inet.c (one place + add include) - * - * /proc/net/wireless is a copy of /proc/net/dev. - * We have a structure for data passed from the driver to /proc/net/wireless - * Too add this, I've modified : -- * net/core/dev.c (two other places) -- * include/linux/netdevice.h (one place) -- * include/linux/proc_fs.h (one place) -+ * # net/core/dev.c (two other places) -+ * # include/linux/netdevice.h (one place) -+ * # include/linux/proc_fs.h (one place) -+ * -+ * New driver API (2001 -> onward) : -+ * ------------------------------- -+ * This file is only concerned with the user space API and common definitions. -+ * The new driver API is defined and documented in : -+ * # include/net/iw_handler.h - * -+ * Note as well that /proc/net/wireless implementation has now moved in : -+ * # include/linux/wireless.c -+ * -+ * Other comments : -+ * -------------- - * Do not add here things that are redundant with other mechanisms - * (drivers init, ifconfig, /proc/net/dev, ...) and with are not - * wireless specific. -@@ -54,16 +68,14 @@ - #include <linux/socket.h> /* for "struct sockaddr" et al */ - #include <linux/if.h> /* for IFNAMSIZ and co... */ - --/**************************** CONSTANTS ****************************/ -- --/* --------------------------- VERSION --------------------------- */ -+/***************************** VERSION *****************************/ - /* - * This constant is used to know the availability of the wireless - * extensions and to know which version of wireless extensions it is - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ --#define WIRELESS_EXT 12 -+#define WIRELESS_EXT 13 - - /* - * Changes : -@@ -123,12 +135,20 @@ - * - Add DEV PRIVATE IOCTL to avoid collisions in SIOCDEVPRIVATE space - * - Add new statistics (frag, retry, beacon) - * - Add average quality (for user space calibration) -+ * -+ * V12 to V13 -+ * ---------- -+ * - Document creation of new driver API. -+ * - Extract union iwreq_data from struct iwreq (for new driver API). -+ * - Rename SIOCSIWNAME as SIOCSIWCOMMIT - */ - -+/**************************** CONSTANTS ****************************/ -+ - /* -------------------------- IOCTL LIST -------------------------- */ - - /* Basic operations */ --#define SIOCSIWNAME 0x8B00 /* Unused */ -+#define SIOCSIWCOMMIT 0x8B00 /* Commit pending changes to driver */ - #define SIOCGIWNAME 0x8B01 /* get name == wireless protocol */ - #define SIOCSIWNWID 0x8B02 /* set network id (the cell) */ - #define SIOCGIWNWID 0x8B03 /* get network id */ -@@ -414,13 +434,49 @@ struct iw_statistics - - /* ------------------------ IOCTL REQUEST ------------------------ */ - /* -+ * This structure defines the payload of an ioctl, and is used -+ * below. -+ * -+ * Note that this structure should fit on the memory footprint -+ * of iwreq (which is the same as ifreq), which mean a max size of -+ * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... -+ * You should check this when increasing the structures defined -+ * above in this file... -+ */ -+union iwreq_data -+{ -+ /* Config - generic */ -+ char name[IFNAMSIZ]; -+ /* Name : used to verify the presence of wireless extensions. -+ * Name of the protocol/provider... */ -+ -+ struct iw_point essid; /* Extended network name */ -+ struct iw_param nwid; /* network id (or domain - the cell) */ -+ struct iw_freq freq; /* frequency or channel : -+ * 0-1000 = channel -+ * > 1000 = frequency in Hz */ -+ -+ struct iw_param sens; /* signal level threshold */ -+ struct iw_param bitrate; /* default bit rate */ -+ struct iw_param txpower; /* default transmit power */ -+ struct iw_param rts; /* RTS threshold threshold */ -+ struct iw_param frag; /* Fragmentation threshold */ -+ __u32 mode; /* Operation mode */ -+ struct iw_param retry; /* Retry limits & lifetime */ -+ -+ struct iw_point encoding; /* Encoding stuff : tokens */ -+ struct iw_param power; /* PM duration/timeout */ -+ -+ struct sockaddr ap_addr; /* Access point address */ -+ -+ struct iw_point data; /* Other large parameters */ -+}; -+ -+/* - * The structure to exchange data for ioctl. - * This structure is the same as 'struct ifreq', but (re)defined for - * convenience... -- * -- * Note that it should fit on the same memory footprint ! -- * You should check this when increasing the above structures (16 octets) -- * 16 octets = 128 bits. Warning, pointers might be 64 bits wide... -+ * Do I need to remind you about structure size (32 octets) ? - */ - struct iwreq - { -@@ -429,35 +485,8 @@ struct iwreq - char ifrn_name[IFNAMSIZ]; /* if name, e.g. "eth0" */ - } ifr_ifrn; - -- /* Data part */ -- union -- { -- /* Config - generic */ -- char name[IFNAMSIZ]; -- /* Name : used to verify the presence of wireless extensions. -- * Name of the protocol/provider... */ -- -- struct iw_point essid; /* Extended network name */ -- struct iw_param nwid; /* network id (or domain - the cell) */ -- struct iw_freq freq; /* frequency or channel : -- * 0-1000 = channel -- * > 1000 = frequency in Hz */ -- -- struct iw_param sens; /* signal level threshold */ -- struct iw_param bitrate; /* default bit rate */ -- struct iw_param txpower; /* default transmit power */ -- struct iw_param rts; /* RTS threshold threshold */ -- struct iw_param frag; /* Fragmentation threshold */ -- __u32 mode; /* Operation mode */ -- struct iw_param retry; /* Retry limits & lifetime */ -- -- struct iw_point encoding; /* Encoding stuff : tokens */ -- struct iw_param power; /* PM duration/timeout */ -- -- struct sockaddr ap_addr; /* Access point address */ -- -- struct iw_point data; /* Other large parameters */ -- } u; -+ /* Data part (defined just above) */ -+ union iwreq_data u; - }; - - /* -------------------------- IOCTL DATA -------------------------- */ -diff -u -p -r --new-file linux/include/net-w12/iw_handler.h linux/include/net/iw_handler.h ---- linux/include/net-w12/iw_handler.h Wed Dec 31 16:00:00 1969 -+++ linux/include/net/iw_handler.h Thu Jan 17 12:16:46 2002 -@@ -0,0 +1,374 @@ -+/* -+ * This file define the new driver API for Wireless Extensions -+ * -+ * Version : 2 6.12.01 -+ * -+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -+ * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved. -+ */ -+ -+#ifndef _IW_HANDLER_H -+#define _IW_HANDLER_H -+ -+/************************** DOCUMENTATION **************************/ -+/* -+ * Initial driver API (1996 -> onward) : -+ * ----------------------------------- -+ * The initial API just sends the IOCTL request received from user space -+ * to the driver (via the driver ioctl handler). The driver has to -+ * handle all the rest... -+ * -+ * The initial API also defines a specific handler in struct net_device -+ * to handle wireless statistics. -+ * -+ * The initial APIs served us well and has proven a reasonably good design. -+ * However, there is a few shortcommings : -+ * o No events, everything is a request to the driver. -+ * o Large ioctl function in driver with gigantic switch statement -+ * (i.e. spaghetti code). -+ * o Driver has to mess up with copy_to/from_user, and in many cases -+ * does it unproperly. Common mistakes are : -+ * * buffer overflows (no checks or off by one checks) -+ * * call copy_to/from_user with irq disabled -+ * o The user space interface is tied to ioctl because of the use -+ * copy_to/from_user. -+ * -+ * New driver API (2001 -> onward) : -+ * ------------------------------- -+ * The new driver API is just a bunch of standard functions (handlers), -+ * each handling a specific Wireless Extension. The driver just export -+ * the list of handler it supports, and those will be called apropriately. -+ * -+ * I tried to keep the main advantage of the previous API (simplicity, -+ * efficiency and light weight), and also I provide a good dose of backward -+ * compatibility (most structures are the same, driver can use both API -+ * simultaneously, ...). -+ * Hopefully, I've also addressed the shortcomming of the initial API. -+ * -+ * The advantage of the new API are : -+ * o Handling of Extensions in driver broken in small contained functions -+ * o Tighter checks of ioctl before calling the driver -+ * o Flexible commit strategy (at least, the start of it) -+ * o Backward compatibility (can be mixed with old API) -+ * o Driver doesn't have to worry about memory and user-space issues -+ * The last point is important for the following reasons : -+ * o You are now able to call the new driver API from any API you -+ * want (including from within other parts of the kernel). -+ * o Common mistakes are avoided (buffer overflow, user space copy -+ * with irq disabled and so on). -+ * -+ * The Drawback of the new API are : -+ * o bloat (especially kernel) -+ * o need to migrate existing drivers to new API -+ * My initial testing shows that the new API adds around 3kB to the kernel -+ * and save between 0 and 5kB from a typical driver. -+ * Also, as all structures and data types are unchanged, the migration is -+ * quite straightforward (but tedious). -+ * -+ * --- -+ * -+ * The new driver API is defined below in this file. User space should -+ * not be aware of what's happening down there... -+ * -+ * A new kernel wrapper is in charge of validating the IOCTLs and calling -+ * the appropriate driver handler. This is implemented in : -+ * # net/core/wireless.c -+ * -+ * The driver export the list of handlers in : -+ * # include/linux/netdevice.h (one place) -+ * -+ * The new driver API is available for WIRELESS_EXT >= 13. -+ * Good luck with migration to the new API ;-) -+ */ -+ -+/* ---------------------- THE IMPLEMENTATION ---------------------- */ -+/* -+ * Some of the choice I've made are pretty controversials. Defining an -+ * API is very much weighting compromises. This goes into some of the -+ * details and the thinking behind the implementation. -+ * -+ * Implementation goals : -+ * -------------------- -+ * The implementation goals were as follow : -+ * o Obvious : you should not need a PhD to understand what's happening, -+ * the benefit is easier maintainance. -+ * o Flexible : it should accomodate a wide variety of driver -+ * implementations and be as flexible as the old API. -+ * o Lean : it should be efficient memory wise to minimise the impact -+ * on kernel footprint. -+ * o Transparent to user space : the large number of user space -+ * applications that use Wireless Extensions should not need -+ * any modifications. -+ * -+ * Array of functions versus Struct of functions -+ * --------------------------------------------- -+ * 1) Having an array of functions allow the kernel code to access the -+ * handler in a single lookup, which is much more efficient (think hash -+ * table here). -+ * 2) The only drawback is that driver writer may put their handler in -+ * the wrong slot. This is trivial to test (I set the frequency, the -+ * bitrate changes). Once the handler is in the proper slot, it will be -+ * there forever, because the array is only extended at the end. -+ * 3) Backward/forward compatibility : adding new handler just require -+ * extending the array, so you can put newer driver in older kernel -+ * without having to patch the kernel code (and vice versa). -+ * -+ * All handler are of the same generic type -+ * ---------------------------------------- -+ * That's a feature !!! -+ * 1) Having a generic handler allow to have generic code, which is more -+ * efficient. If each of the handler was individually typed I would need -+ * to add a big switch in the kernel (== more bloat). This solution is -+ * more scalable, adding new Wireless Extensions doesn't add new code. -+ * 2) You can use the same handler in different slots of the array. For -+ * hardware, it may be more efficient or logical to handle multiple -+ * Wireless Extensions with a single function, and the API allow you to -+ * do that. (An example would be a single record on the card to control -+ * both bitrate and frequency, the handler would read the old record, -+ * modify it according to info->cmd and rewrite it). -+ * -+ * Functions prototype uses union iwreq_data -+ * ----------------------------------------- -+ * Some would have prefered functions defined this way : -+ * static int mydriver_ioctl_setrate(struct net_device *dev, -+ * long rate, int auto) -+ * 1) The kernel code doesn't "validate" the content of iwreq_data, and -+ * can't do it (different hardware may have different notion of what a -+ * valid frequency is), so we don't pretend that we do it. -+ * 2) The above form is not extendable. If I want to add a flag (for -+ * example to distinguish setting max rate and basic rate), I would -+ * break the prototype. Using iwreq_data is more flexible. -+ * 3) Also, the above form is not generic (see above). -+ * 4) I don't expect driver developper using the wrong field of the -+ * union (Doh !), so static typechecking doesn't add much value. -+ * 5) Lastly, you can skip the union by doing : -+ * static int mydriver_ioctl_setrate(struct net_device *dev, -+ * struct iw_request_info *info, -+ * struct iw_param *rrq, -+ * char *extra) -+ * And then adding the handler in the array like this : -+ * (iw_handler) mydriver_ioctl_setrate, // SIOCSIWRATE -+ * -+ * Using functions and not a registry -+ * ---------------------------------- -+ * Another implementation option would have been for every instance to -+ * define a registry (a struct containing all the Wireless Extensions) -+ * and only have a function to commit the registry to the hardware. -+ * 1) This approach can be emulated by the current code, but not -+ * vice versa. -+ * 2) Some drivers don't keep any configuration in the driver, for them -+ * adding such a registry would be a significant bloat. -+ * 3) The code to translate from Wireless Extension to native format is -+ * needed anyway, so it would not reduce significantely the amount of code. -+ * 4) The current approach only selectively translate Wireless Extensions -+ * to native format and only selectively set, whereas the registry approach -+ * would require to translate all WE and set all parameters for any single -+ * change. -+ * 5) For many Wireless Extensions, the GET operation return the current -+ * dynamic value, not the value that was set. -+ * -+ * This header is <net/iw_handler.h> -+ * --------------------------------- -+ * 1) This header is kernel space only and should not be exported to -+ * user space. Headers in "include/linux/" are exported, headers in -+ * "include/net/" are not. -+ * -+ * Mixed 32/64 bit issues -+ * ---------------------- -+ * The Wireless Extensions are designed to be 64 bit clean, by using only -+ * datatypes with explicit storage size. -+ * There are some issues related to kernel and user space using different -+ * memory model, and in particular 64bit kernel with 32bit user space. -+ * The problem is related to struct iw_point, that contains a pointer -+ * that *may* need to be translated. -+ * This is quite messy. The new API doesn't solve this problem (it can't), -+ * but is a step in the right direction : -+ * 1) Meta data about each ioctl is easily available, so we know what type -+ * of translation is needed. -+ * 2) The move of data between kernel and user space is only done in a single -+ * place in the kernel, so adding specific hooks in there is possible. -+ * 3) In the long term, it allows to move away from using ioctl as the -+ * user space API. -+ * -+ * So many comments and so few code -+ * -------------------------------- -+ * That's a feature. Comments won't bloat the resulting kernel binary. -+ */ -+ -+/***************************** INCLUDES *****************************/ -+ -+#include <linux/wireless.h> /* IOCTL user space API */ -+ -+/***************************** VERSION *****************************/ -+/* -+ * This constant is used to know which version of the driver API is -+ * available. Hopefully, this will be pretty stable and no changes -+ * will be needed... -+ * I just plan to increment with each new version. -+ */ -+#define IW_HANDLER_VERSION 2 -+ -+/**************************** CONSTANTS ****************************/ -+ -+/* Special error message for the driver to indicate that we -+ * should do a commit after return from the iw_handler */ -+#define EIWCOMMIT EINPROGRESS -+ -+/* Flags available in struct iw_request_info */ -+#define IW_REQUEST_FLAG_NONE 0x0000 /* No flag so far */ -+ -+/* Type of headers we know about (basically union iwreq_data) */ -+#define IW_HEADER_TYPE_NULL 0 /* Not available */ -+#define IW_HEADER_TYPE_CHAR 2 /* char [IFNAMSIZ] */ -+#define IW_HEADER_TYPE_UINT 4 /* __u32 */ -+#define IW_HEADER_TYPE_FREQ 5 /* struct iw_freq */ -+#define IW_HEADER_TYPE_POINT 6 /* struct iw_point */ -+#define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */ -+#define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */ -+ -+/* Handling flags */ -+/* Most are not implemented. I just use them as a reminder of some -+ * cool features we might need one day ;-) */ -+#define IW_DESCR_FLAG_NONE 0x0000 /* Obvious */ -+/* Wrapper level flags */ -+#define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */ -+#define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ -+#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET request is ROOT only */ -+/* Driver level flags */ -+#define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ -+ -+/****************************** TYPES ******************************/ -+ -+/* ----------------------- WIRELESS HANDLER ----------------------- */ -+/* -+ * A wireless handler is just a standard function, that looks like the -+ * ioctl handler. -+ * We also define there how a handler list look like... As the Wireless -+ * Extension space is quite dense, we use a simple array, which is faster -+ * (that's the perfect hash table ;-). -+ */ -+ -+/* -+ * Meta data about the request passed to the iw_handler. -+ * Most handlers can safely ignore what's in there. -+ * The 'cmd' field might come handy if you want to use the same handler -+ * for multiple command... -+ * This struct is also my long term insurance. I can add new fields here -+ * without breaking the prototype of iw_handler... -+ */ -+struct iw_request_info -+{ -+ __u16 cmd; /* Wireless Extension command */ -+ __u16 flags; /* More to come ;-) */ -+}; -+ -+/* -+ * This is how a function handling a Wireless Extension should look -+ * like (both get and set, standard and private). -+ */ -+typedef int (*iw_handler)(struct net_device *dev, struct iw_request_info *info, -+ union iwreq_data *wrqu, char *extra); -+ -+/* -+ * This define all the handler that the driver export. -+ * As you need only one per driver type, please use a static const -+ * shared by all driver instances... Same for the members... -+ * This will be linked from net_device in <linux/netdevice.h> -+ */ -+struct iw_handler_def -+{ -+ /* Number of handlers defined (more precisely, index of the -+ * last defined handler + 1) */ -+ __u16 num_standard; -+ __u16 num_private; -+ /* Number of private arg description */ -+ __u16 num_private_args; -+ -+ /* Array of handlers for standard ioctls -+ * We will call dev->wireless_handlers->standard[ioctl - SIOCSIWNAME] -+ */ -+ iw_handler * standard; -+ -+ /* Array of handlers for private ioctls -+ * Will call dev->wireless_handlers->private[ioctl - SIOCIWFIRSTPRIV] -+ */ -+ iw_handler * private; -+ -+ /* Arguments of private handler. This one is just a list, so you -+ * can put it in any order you want and should not leave holes... -+ * We will automatically export that to user space... */ -+ struct iw_priv_args * private_args; -+ -+ /* In the long term, get_wireless_stats will move from -+ * 'struct net_device' to here, to minimise bloat. */ -+}; -+ -+/* ----------------------- WIRELESS EVENTS ----------------------- */ -+/* -+ * Currently we don't support events, so let's just plan for the -+ * future... -+ */ -+ -+/* -+ * A Wireless Event. -+ */ -+// How do we define short header ? We don't want a flag on length. -+// Probably a flag on event ? Highest bit to zero... -+struct iw_event -+{ -+ __u16 length; /* Lenght of this stuff */ -+ __u16 event; /* Wireless IOCTL */ -+ union iwreq_data header; /* IOCTL fixed payload */ -+ char extra[0]; /* Optional IOCTL data */ -+}; -+ -+/* ---------------------- IOCTL DESCRIPTION ---------------------- */ -+/* -+ * One of the main goal of the new interface is to deal entirely with -+ * user space/kernel space memory move. -+ * For that, we need to know : -+ * o if iwreq is a pointer or contain the full data -+ * o what is the size of the data to copy -+ * -+ * For private IOCTLs, we use the same rules as used by iwpriv and -+ * defined in struct iw_priv_args. -+ * -+ * For standard IOCTLs, things are quite different and we need to -+ * use the stuctures below. Actually, this struct is also more -+ * efficient, but that's another story... -+ */ -+ -+/* -+ * Describe how a standard IOCTL looks like. -+ */ -+struct iw_ioctl_description -+{ -+ __u8 header_type; /* NULL, iw_point or other */ -+ __u8 token_type; /* Future */ -+ __u16 token_size; /* Granularity of payload */ -+ __u16 min_tokens; /* Min acceptable token number */ -+ __u16 max_tokens; /* Max acceptable token number */ -+ __u32 flags; /* Special handling of the request */ -+}; -+ -+/* Need to think of short header translation table. Later. */ -+ -+/**************************** PROTOTYPES ****************************/ -+/* -+ * Functions part of the Wireless Extensions (defined in net/core/wireless.c). -+ * Those may be called only within the kernel. -+ */ -+ -+/* First : function strictly used inside the kernel */ -+ -+/* Handle /proc/net/wireless, called in net/code/dev.c */ -+extern int dev_get_wireless_info(char * buffer, char **start, off_t offset, -+ int length); -+ -+/* Handle IOCTLs, called in net/code/dev.c */ -+extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); -+ -+/* Second : functions that may be called by driver modules */ -+/* None yet */ -+ -+#endif /* _LINUX_WIRELESS_H */ -diff -u -p -r --new-file linux/net/core-w12/Makefile linux/net/core/Makefile ---- linux/net/core-w12/Makefile Tue Oct 30 15:08:12 2001 -+++ linux/net/core/Makefile Thu Jan 17 11:06:07 2002 -@@ -26,5 +26,8 @@ obj-$(CONFIG_NET) += dev.o dev_mcast.o d - obj-$(CONFIG_NETFILTER) += netfilter.o - obj-$(CONFIG_NET_DIVERT) += dv.o - obj-$(CONFIG_NET_PROFILE) += profile.o -+obj-$(CONFIG_NET_RADIO) += wireless.o -+# Ugly. I wish all wireless drivers were moved in drivers/net/wireless -+obj-$(CONFIG_NET_PCMCIA_RADIO) += wireless.o - - include $(TOPDIR)/Rules.make -diff -u -p -r --new-file linux/net/core-w12/dev.c linux/net/core/dev.c ---- linux/net/core-w12/dev.c Wed Nov 7 14:39:36 2001 -+++ linux/net/core/dev.c Thu Jan 17 11:06:07 2002 -@@ -102,6 +102,7 @@ - #include <linux/module.h> - #if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) - #include <linux/wireless.h> /* Note : will define WIRELESS_EXT */ -+#include <net/iw_handler.h> - #endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ - #ifdef CONFIG_PLIP - extern int plip_init(void); -@@ -1796,122 +1797,6 @@ static int dev_proc_stats(char *buffer, - #endif /* CONFIG_PROC_FS */ - - --#ifdef WIRELESS_EXT --#ifdef CONFIG_PROC_FS -- --/* -- * Print one entry of /proc/net/wireless -- * This is a clone of /proc/net/dev (just above) -- */ --static int sprintf_wireless_stats(char *buffer, struct net_device *dev) --{ -- /* Get stats from the driver */ -- struct iw_statistics *stats = (dev->get_wireless_stats ? -- dev->get_wireless_stats(dev) : -- (struct iw_statistics *) NULL); -- int size; -- -- if (stats != (struct iw_statistics *) NULL) { -- size = sprintf(buffer, -- "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n", -- dev->name, -- stats->status, -- stats->qual.qual, -- stats->qual.updated & 1 ? '.' : ' ', -- stats->qual.level, -- stats->qual.updated & 2 ? '.' : ' ', -- stats->qual.noise, -- stats->qual.updated & 4 ? '.' : ' ', -- stats->discard.nwid, -- stats->discard.code, -- stats->discard.fragment, -- stats->discard.retries, -- stats->discard.misc, -- stats->miss.beacon); -- stats->qual.updated = 0; -- } -- else -- size = 0; -- -- return size; --} -- --/* -- * Print info for /proc/net/wireless (print all entries) -- * This is a clone of /proc/net/dev (just above) -- */ --static int dev_get_wireless_info(char * buffer, char **start, off_t offset, -- int length) --{ -- int len = 0; -- off_t begin = 0; -- off_t pos = 0; -- int size; -- -- struct net_device * dev; -- -- size = sprintf(buffer, -- "Inter-| sta-| Quality | Discarded packets | Missed\n" -- " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" -- ); -- -- pos += size; -- len += size; -- -- read_lock(&dev_base_lock); -- for (dev = dev_base; dev != NULL; dev = dev->next) { -- size = sprintf_wireless_stats(buffer + len, dev); -- len += size; -- pos = begin + len; -- -- if (pos < offset) { -- len = 0; -- begin = pos; -- } -- if (pos > offset + length) -- break; -- } -- read_unlock(&dev_base_lock); -- -- *start = buffer + (offset - begin); /* Start of wanted data */ -- len -= (offset - begin); /* Start slop */ -- if (len > length) -- len = length; /* Ending slop */ -- if (len < 0) -- len = 0; -- -- return len; --} --#endif /* CONFIG_PROC_FS */ -- --/* -- * Allow programatic access to /proc/net/wireless even if /proc -- * doesn't exist... Also more efficient... -- */ --static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr) --{ -- /* Get stats from the driver */ -- struct iw_statistics *stats = (dev->get_wireless_stats ? -- dev->get_wireless_stats(dev) : -- (struct iw_statistics *) NULL); -- -- if (stats != (struct iw_statistics *) NULL) { -- struct iwreq * wrq = (struct iwreq *)ifr; -- -- /* Copy statistics to the user buffer */ -- if(copy_to_user(wrq->u.data.pointer, stats, -- sizeof(struct iw_statistics))) -- return -EFAULT; -- -- /* Check if we need to clear the update flag */ -- if(wrq->u.data.flags != 0) -- stats->qual.updated = 0; -- return(0); -- } else -- return -EOPNOTSUPP; --} --#endif /* WIRELESS_EXT */ -- - /** - * netdev_set_master - set up master/slave pair - * @slave: slave device -@@ -2209,11 +2094,6 @@ static int dev_ifsioc(struct ifreq *ifr, - notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); - return 0; - --#ifdef WIRELESS_EXT -- case SIOCGIWSTATS: -- return dev_iwstats(dev, ifr); --#endif /* WIRELESS_EXT */ -- - /* - * Unknown or private ioctl - */ -@@ -2239,17 +2119,6 @@ static int dev_ifsioc(struct ifreq *ifr, - return -EOPNOTSUPP; - } - --#ifdef WIRELESS_EXT -- if (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) { -- if (dev->do_ioctl) { -- if (!netif_device_present(dev)) -- return -ENODEV; -- return dev->do_ioctl(dev, ifr, cmd); -- } -- return -EOPNOTSUPP; -- } --#endif /* WIRELESS_EXT */ -- - } - return -EINVAL; - } -@@ -2431,7 +2300,8 @@ int dev_ioctl(unsigned int cmd, void *ar - } - dev_load(ifr.ifr_name); - rtnl_lock(); -- ret = dev_ifsioc(&ifr, cmd); -+ /* Follow me in net/core/wireless.c */ -+ ret = wireless_process_ioctl(&ifr, cmd); - rtnl_unlock(); - if (!ret && IW_IS_GET(cmd) && - copy_to_user(arg, &ifr, sizeof(struct ifreq))) -@@ -2856,6 +2726,7 @@ int __init net_dev_init(void) - proc_net_create("dev", 0, dev_get_info); - create_proc_read_entry("net/softnet_stat", 0, 0, dev_proc_stats, NULL); - #ifdef WIRELESS_EXT -+ /* Available in net/core/wireless.c */ - proc_net_create("wireless", 0, dev_get_wireless_info); - #endif /* WIRELESS_EXT */ - #endif /* CONFIG_PROC_FS */ -diff -u -p -r --new-file linux/net/core-w12/wireless.c linux/net/core/wireless.c ---- linux/net/core-w12/wireless.c Wed Dec 31 16:00:00 1969 -+++ linux/net/core/wireless.c Mon Jan 21 11:13:23 2002 -@@ -0,0 +1,733 @@ -+/* -+ * This file implement the Wireless Extensions APIs. -+ * -+ * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -+ * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. -+ * -+ * (As all part of the Linux kernel, this file is GPL) -+ */ -+ -+/************************** DOCUMENTATION **************************/ -+/* -+ * API definition : -+ * -------------- -+ * See <linux/wireless.h> for details of the APIs and the rest. -+ * -+ * History : -+ * ------- -+ * -+ * v1 - 5.12.01 - Jean II -+ * o Created this file. -+ * -+ * v2 - 13.12.01 - Jean II -+ * o Move /proc/net/wireless stuff from net/core/dev.c to here -+ * o Make Wireless Extension IOCTLs go through here -+ * o Added iw_handler handling ;-) -+ * o Added standard ioctl description -+ * o Initial dumb commit strategy based on orinoco.c -+ */ -+ -+/***************************** INCLUDES *****************************/ -+ -+#include <asm/uaccess.h> /* copy_to_user() */ -+#include <linux/config.h> /* Not needed ??? */ -+#include <linux/types.h> /* off_t */ -+#include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ -+ -+#include <linux/wireless.h> /* Pretty obvious */ -+#include <net/iw_handler.h> /* New driver API */ -+ -+/**************************** CONSTANTS ****************************/ -+ -+/* This will be turned on later on... */ -+#undef WE_STRICT_WRITE /* Check write buffer size */ -+ -+/* Debuging stuff */ -+#undef WE_IOCTL_DEBUG /* Debug IOCTL API */ -+ -+/************************* GLOBAL VARIABLES *************************/ -+/* -+ * You should not use global variables, because or re-entrancy. -+ * On our case, it's only const, so it's OK... -+ */ -+static const struct iw_ioctl_description standard_ioctl[] = { -+ /* SIOCSIWCOMMIT (internal) */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCGIWNAME */ -+ { IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -+ /* SIOCSIWNWID */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, -+ /* SIOCGIWNWID */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -+ /* SIOCSIWFREQ */ -+ { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, -+ /* SIOCGIWFREQ */ -+ { IW_HEADER_TYPE_FREQ, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -+ /* SIOCSIWMODE */ -+ { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_EVENT}, -+ /* SIOCGIWMODE */ -+ { IW_HEADER_TYPE_UINT, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -+ /* SIOCSIWSENS */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWSENS */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCSIWRANGE */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCGIWRANGE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, sizeof(struct iw_range), IW_DESCR_FLAG_DUMP}, -+ /* SIOCSIWPRIV */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCGIWPRIV (handled directly by us) */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCSIWSTATS */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCGIWSTATS (handled directly by us) */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -+ /* SIOCSIWSPY */ -+ { IW_HEADER_TYPE_POINT, 0, sizeof(struct sockaddr), 0, IW_MAX_SPY, 0}, -+ /* SIOCGIWSPY */ -+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_SPY, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCSIWAP */ -+ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, -+ /* SIOCGIWAP */ -+ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCGIWAPLIST */ -+ { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCSIWESSID */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT}, -+ /* SIOCGIWESSID */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP}, -+ /* SIOCSIWNICKN */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, -+ /* SIOCGIWNICKN */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* -- hole -- */ -+ { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCSIWRATE */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWRATE */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCSIWRTS */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWRTS */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCSIWFRAG */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWFRAG */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCSIWTXPOW */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWTXPOW */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCSIWRETRY */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWRETRY */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCSIWENCODE */ -+ { IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT}, -+ /* SIOCGIWENCODE */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT}, -+ /* SIOCSIWPOWER */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWPOWER */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+}; -+ -+/* Size (in bytes) of the various private data types */ -+char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; -+ -+/************************ COMMON SUBROUTINES ************************/ -+/* -+ * Stuff that may be used in various place or doesn't fit in one -+ * of the section below. -+ */ -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Return the driver handler associated with a specific Wireless Extension. -+ * Called from various place, so make sure it remains efficient. -+ */ -+static inline iw_handler get_handler(struct net_device *dev, -+ unsigned int cmd) -+{ -+ unsigned int index; /* MUST be unsigned */ -+ -+ /* Check if we have some wireless handlers defined */ -+ if(dev->wireless_handlers == NULL) -+ return NULL; -+ -+ /* Try as a standard command */ -+ index = cmd - SIOCIWFIRST; -+ if(index < dev->wireless_handlers->num_standard) -+ return dev->wireless_handlers->standard[index]; -+ -+ /* Try as a private command */ -+ index = cmd - SIOCIWFIRSTPRIV; -+ if(index < dev->wireless_handlers->num_private) -+ return dev->wireless_handlers->private[index]; -+ -+ /* Not found */ -+ return NULL; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Get statistics out of the driver -+ */ -+static inline struct iw_statistics *get_wireless_stats(struct net_device *dev) -+{ -+ return (dev->get_wireless_stats ? -+ dev->get_wireless_stats(dev) : -+ (struct iw_statistics *) NULL); -+ /* In the future, get_wireless_stats may move from 'struct net_device' -+ * to 'struct iw_handler_def', to de-bloat struct net_device. -+ * Definitely worse a thought... */ -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Call the commit handler in the driver -+ * (if exist and if conditions are right) -+ * -+ * Note : our current commit strategy is currently pretty dumb, -+ * but we will be able to improve on that... -+ * The goal is to try to agreagate as many changes as possible -+ * before doing the commit. Drivers that will define a commit handler -+ * are usually those that need a reset after changing parameters, so -+ * we want to minimise the number of reset. -+ * A cool idea is to use a timer : at each "set" command, we re-set the -+ * timer, when the timer eventually fires, we call the driver. -+ * Hopefully, more on that later. -+ * -+ * Also, I'm waiting to see how many people will complain about the -+ * netif_running(dev) test. I'm open on that one... -+ * Hopefully, the driver will remember to do a commit in "open()" ;-) -+ */ -+static inline int call_commit_handler(struct net_device * dev) -+{ -+ if((netif_running(dev)) && -+ (dev->wireless_handlers->standard[0] != NULL)) { -+ /* Call the commit handler on the driver */ -+ return dev->wireless_handlers->standard[0](dev, NULL, -+ NULL, NULL); -+ } else -+ return 0; /* Command completed successfully */ -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Number of private arguments -+ */ -+static inline int get_priv_size(__u16 args) -+{ -+ int num = args & IW_PRIV_SIZE_MASK; -+ int type = (args & IW_PRIV_TYPE_MASK) >> 12; -+ -+ return num * priv_type_size[type]; -+} -+ -+ -+/******************** /proc/net/wireless SUPPORT ********************/ -+/* -+ * The /proc/net/wireless file is a human readable user-space interface -+ * exporting various wireless specific statistics from the wireless devices. -+ * This is the most popular part of the Wireless Extensions ;-) -+ * -+ * This interface is a pure clone of /proc/net/dev (in net/core/dev.c). -+ * The content of the file is basically the content of "struct iw_statistics". -+ */ -+ -+#ifdef CONFIG_PROC_FS -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Print one entry (line) of /proc/net/wireless -+ */ -+static inline int sprintf_wireless_stats(char *buffer, struct net_device *dev) -+{ -+ /* Get stats from the driver */ -+ struct iw_statistics *stats; -+ int size; -+ -+ stats = get_wireless_stats(dev); -+ if (stats != (struct iw_statistics *) NULL) { -+ size = sprintf(buffer, -+ "%6s: %04x %3d%c %3d%c %3d%c %6d %6d %6d %6d %6d %6d\n", -+ dev->name, -+ stats->status, -+ stats->qual.qual, -+ stats->qual.updated & 1 ? '.' : ' ', -+ stats->qual.level, -+ stats->qual.updated & 2 ? '.' : ' ', -+ stats->qual.noise, -+ stats->qual.updated & 4 ? '.' : ' ', -+ stats->discard.nwid, -+ stats->discard.code, -+ stats->discard.fragment, -+ stats->discard.retries, -+ stats->discard.misc, -+ stats->miss.beacon); -+ stats->qual.updated = 0; -+ } -+ else -+ size = 0; -+ -+ return size; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Print info for /proc/net/wireless (print all entries) -+ */ -+int dev_get_wireless_info(char * buffer, char **start, off_t offset, -+ int length) -+{ -+ int len = 0; -+ off_t begin = 0; -+ off_t pos = 0; -+ int size; -+ -+ struct net_device * dev; -+ -+ size = sprintf(buffer, -+ "Inter-| sta-| Quality | Discarded packets | Missed\n" -+ " face | tus | link level noise | nwid crypt frag retry misc | beacon\n" -+ ); -+ -+ pos += size; -+ len += size; -+ -+ read_lock(&dev_base_lock); -+ for (dev = dev_base; dev != NULL; dev = dev->next) { -+ size = sprintf_wireless_stats(buffer + len, dev); -+ len += size; -+ pos = begin + len; -+ -+ if (pos < offset) { -+ len = 0; -+ begin = pos; -+ } -+ if (pos > offset + length) -+ break; -+ } -+ read_unlock(&dev_base_lock); -+ -+ *start = buffer + (offset - begin); /* Start of wanted data */ -+ len -= (offset - begin); /* Start slop */ -+ if (len > length) -+ len = length; /* Ending slop */ -+ if (len < 0) -+ len = 0; -+ -+ return len; -+} -+#endif /* CONFIG_PROC_FS */ -+ -+/************************** IOCTL SUPPORT **************************/ -+/* -+ * The original user space API to configure all those Wireless Extensions -+ * is through IOCTLs. -+ * In there, we check if we need to call the new driver API (iw_handler) -+ * or just call the driver ioctl handler. -+ */ -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Allow programatic access to /proc/net/wireless even if /proc -+ * doesn't exist... Also more efficient... -+ */ -+static inline int dev_iwstats(struct net_device *dev, struct ifreq *ifr) -+{ -+ /* Get stats from the driver */ -+ struct iw_statistics *stats; -+ -+ stats = get_wireless_stats(dev); -+ if (stats != (struct iw_statistics *) NULL) { -+ struct iwreq * wrq = (struct iwreq *)ifr; -+ -+ /* Copy statistics to the user buffer */ -+ if(copy_to_user(wrq->u.data.pointer, stats, -+ sizeof(struct iw_statistics))) -+ return -EFAULT; -+ -+ /* Check if we need to clear the update flag */ -+ if(wrq->u.data.flags != 0) -+ stats->qual.updated = 0; -+ return 0; -+ } else -+ return -EOPNOTSUPP; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Export the driver private handler definition -+ * They will be picked up by tools like iwpriv... -+ */ -+static inline int ioctl_export_private(struct net_device * dev, -+ struct ifreq * ifr) -+{ -+ struct iwreq * iwr = (struct iwreq *) ifr; -+ -+ /* Check if the driver has something to export */ -+ if((dev->wireless_handlers->num_private_args == 0) || -+ (dev->wireless_handlers->private_args == NULL)) -+ return -EOPNOTSUPP; -+ -+ /* Check NULL pointer */ -+ if(iwr->u.data.pointer == NULL) -+ return -EFAULT; -+#ifdef WE_STRICT_WRITE -+ /* Check if there is enough buffer up there */ -+ if(iwr->u.data.length < (SIOCIWLASTPRIV - SIOCIWFIRSTPRIV + 1)) -+ return -E2BIG; -+#endif /* WE_STRICT_WRITE */ -+ -+ /* Set the number of available ioctls. */ -+ iwr->u.data.length = dev->wireless_handlers->num_private_args; -+ -+ /* Copy structure to the user buffer. */ -+ if (copy_to_user(iwr->u.data.pointer, -+ dev->wireless_handlers->private_args, -+ sizeof(struct iw_priv_args) * iwr->u.data.length)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Wrapper to call a standard Wireless Extension handler. -+ * We do various checks and also take care of moving data between -+ * user space and kernel space. -+ */ -+static inline int ioctl_standard_call(struct net_device * dev, -+ struct ifreq * ifr, -+ unsigned int cmd, -+ iw_handler handler) -+{ -+ struct iwreq * iwr = (struct iwreq *) ifr; -+ const struct iw_ioctl_description * descr; -+ struct iw_request_info info; -+ int ret = -EINVAL; -+ -+ /* Get the description of the IOCTL */ -+ descr = &(standard_ioctl[cmd - SIOCIWFIRST]); -+ -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n", -+ ifr->ifr_name, cmd); -+ printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -+#endif /* WE_IOCTL_DEBUG */ -+ -+ /* Prepare the call */ -+ info.cmd = cmd; -+ info.flags = 0; -+ -+ /* Check if we have a pointer to user space data or not */ -+ if(descr->header_type != IW_HEADER_TYPE_POINT) { -+ /* No extra arguments. Trivial to handle */ -+ ret = handler(dev, &info, &(iwr->u), NULL); -+ } else { -+ char * extra; -+ int err; -+ -+ /* Check what user space is giving us */ -+ if(IW_IS_SET(cmd)) { -+ /* Check NULL pointer */ -+ if((iwr->u.data.pointer == NULL) && -+ (iwr->u.data.length != 0)) -+ return -EFAULT; -+ /* Check if number of token fits within bounds */ -+ if(iwr->u.data.length > descr->max_tokens) -+ return -E2BIG; -+ if(iwr->u.data.length < descr->min_tokens) -+ return -EINVAL; -+ } else { -+ /* Check NULL pointer */ -+ if(iwr->u.data.pointer == NULL) -+ return -EFAULT; -+#ifdef WE_STRICT_WRITE -+ /* Check if there is enough buffer up there */ -+ if(iwr->u.data.length < descr->max_tokens) -+ return -E2BIG; -+#endif /* WE_STRICT_WRITE */ -+ } -+ -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "Malloc %d bytes\n", -+ descr->max_tokens * descr->token_size); -+#endif /* WE_IOCTL_DEBUG */ -+ -+ /* Always allocate for max space. Easier, and won't last -+ * long... */ -+ extra = kmalloc(descr->max_tokens * descr->token_size, -+ GFP_KERNEL); -+ if (extra == NULL) { -+ return -ENOMEM; -+ } -+ -+ /* If it is a SET, get all the extra data in here */ -+ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { -+ err = copy_from_user(extra, iwr->u.data.pointer, -+ iwr->u.data.length * -+ descr->token_size); -+ if (err) { -+ kfree(extra); -+ return -EFAULT; -+ } -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "Got %d bytes\n", -+ iwr->u.data.length * descr->token_size); -+#endif /* WE_IOCTL_DEBUG */ -+ } -+ -+ /* Call the handler */ -+ ret = handler(dev, &info, &(iwr->u), extra); -+ -+ /* If we have something to return to the user */ -+ if (!ret && IW_IS_GET(cmd)) { -+ err = copy_to_user(iwr->u.data.pointer, extra, -+ iwr->u.data.length * -+ descr->token_size); -+ if (err) -+ ret = -EFAULT; -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "Wrote %d bytes\n", -+ iwr->u.data.length * descr->token_size); -+#endif /* WE_IOCTL_DEBUG */ -+ } -+ -+ /* Cleanup - I told you it wasn't that long ;-) */ -+ kfree(extra); -+ } -+ -+ /* Call commit handler if needed and defined */ -+ if(ret == -EIWCOMMIT) -+ ret = call_commit_handler(dev); -+ -+ /* Here, we will generate the appropriate event if needed */ -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Wrapper to call a private Wireless Extension handler. -+ * We do various checks and also take care of moving data between -+ * user space and kernel space. -+ * It's not as nice and slimline as the standard wrapper. The cause -+ * is struct iw_priv_args, which was not really designed for the -+ * job we are going here. -+ * -+ * IMPORTANT : This function prevent to set and get data on the same -+ * IOCTL and enforce the SET/GET convention. Not doing it would be -+ * far too hairy... -+ * If you need to set and get data at the same time, please don't use -+ * a iw_handler but process it in your ioctl handler (i.e. use the -+ * old driver API). -+ */ -+static inline int ioctl_private_call(struct net_device * dev, -+ struct ifreq * ifr, -+ unsigned int cmd, -+ iw_handler handler) -+{ -+ struct iwreq * iwr = (struct iwreq *) ifr; -+ struct iw_priv_args * descr = NULL; -+ struct iw_request_info info; -+ int extra_size = 0; -+ int i; -+ int ret = -EINVAL; -+ -+ /* Get the description of the IOCTL */ -+ for(i = 0; i < dev->wireless_handlers->num_private_args; i++) -+ if(cmd == dev->wireless_handlers->private_args[i].cmd) { -+ descr = &(dev->wireless_handlers->private_args[i]); -+ break; -+ } -+ -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "%s : Found private handler for 0x%04X\n", -+ ifr->ifr_name, cmd); -+ if(descr) { -+ printk(KERN_DEBUG "Name %s, set %X, get %X\n", -+ descr->name, descr->set_args, descr->get_args); -+ } -+#endif /* WE_IOCTL_DEBUG */ -+ -+ /* Compute the size of the set/get arguments */ -+ if(descr != NULL) { -+ if(IW_IS_SET(cmd)) { -+ /* Size of set arguments */ -+ extra_size = get_priv_size(descr->set_args); -+ -+ /* Does it fits in iwr ? */ -+ if((descr->set_args & IW_PRIV_SIZE_FIXED) && -+ (extra_size < IFNAMSIZ)) -+ extra_size = 0; -+ } else { -+ /* Size of set arguments */ -+ extra_size = get_priv_size(descr->get_args); -+ -+ /* Does it fits in iwr ? */ -+ if((descr->get_args & IW_PRIV_SIZE_FIXED) && -+ (extra_size < IFNAMSIZ)) -+ extra_size = 0; -+ } -+ } -+ -+ /* Prepare the call */ -+ info.cmd = cmd; -+ info.flags = 0; -+ -+ /* Check if we have a pointer to user space data or not. */ -+ if(extra_size == 0) { -+ /* No extra arguments. Trivial to handle */ -+ ret = handler(dev, &info, &(iwr->u), (char *) &(iwr->u)); -+ } else { -+ char * extra; -+ int err; -+ -+ /* Check what user space is giving us */ -+ if(IW_IS_SET(cmd)) { -+ /* Check NULL pointer */ -+ if((iwr->u.data.pointer == NULL) && -+ (iwr->u.data.length != 0)) -+ return -EFAULT; -+ -+ /* Does it fits within bounds ? */ -+ if(iwr->u.data.length > (descr->set_args & -+ IW_PRIV_SIZE_MASK)) -+ return -E2BIG; -+ } else { -+ /* Check NULL pointer */ -+ if(iwr->u.data.pointer == NULL) -+ return -EFAULT; -+ } -+ -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "Malloc %d bytes\n", extra_size); -+#endif /* WE_IOCTL_DEBUG */ -+ -+ /* Always allocate for max space. Easier, and won't last -+ * long... */ -+ extra = kmalloc(extra_size, GFP_KERNEL); -+ if (extra == NULL) { -+ return -ENOMEM; -+ } -+ -+ /* If it is a SET, get all the extra data in here */ -+ if(IW_IS_SET(cmd) && (iwr->u.data.length != 0)) { -+ err = copy_from_user(extra, iwr->u.data.pointer, -+ extra_size); -+ if (err) { -+ kfree(extra); -+ return -EFAULT; -+ } -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "Got %d elem\n", iwr->u.data.length); -+#endif /* WE_IOCTL_DEBUG */ -+ } -+ -+ /* Call the handler */ -+ ret = handler(dev, &info, &(iwr->u), extra); -+ -+ /* If we have something to return to the user */ -+ if (!ret && IW_IS_GET(cmd)) { -+ err = copy_to_user(iwr->u.data.pointer, extra, -+ extra_size); -+ if (err) -+ ret = -EFAULT; -+#ifdef WE_IOCTL_DEBUG -+ printk(KERN_DEBUG "Wrote %d elem\n", -+ iwr->u.data.length); -+#endif /* WE_IOCTL_DEBUG */ -+ } -+ -+ /* Cleanup - I told you it wasn't that long ;-) */ -+ kfree(extra); -+ } -+ -+ -+ /* Call commit handler if needed and defined */ -+ if(ret == -EIWCOMMIT) -+ ret = call_commit_handler(dev); -+ -+ return ret; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Main IOCTl dispatcher. Called from the main networking code -+ * (dev_ioctl() in net/core/dev.c). -+ * Check the type of IOCTL and call the appropriate wrapper... -+ */ -+int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd) -+{ -+ struct net_device *dev; -+ iw_handler handler; -+ -+ /* Permissions are already checked in dev_ioctl() before calling us. -+ * The copy_to/from_user() of ifr is also dealt with in there */ -+ -+ /* Make sure the device exist */ -+ if ((dev = __dev_get_by_name(ifr->ifr_name)) == NULL) -+ return -ENODEV; -+ -+ /* A bunch of special cases, then the generic case... -+ * Note that 'cmd' is already filtered in dev_ioctl() with -+ * (cmd >= SIOCIWFIRST && cmd <= SIOCIWLAST) */ -+ switch(cmd) -+ { -+ case SIOCGIWSTATS: -+ /* Get Wireless Stats */ -+ return dev_iwstats(dev, ifr); -+ -+ case SIOCGIWPRIV: -+ /* Check if we have some wireless handlers defined */ -+ if(dev->wireless_handlers != NULL) { -+ /* We export to user space the definition of -+ * the private handler ourselves */ -+ return ioctl_export_private(dev, ifr); -+ } -+ // ## Fall-through for old API ## -+ default: -+ /* Generic IOCTL */ -+ /* Basic check */ -+ if (!netif_device_present(dev)) -+ return -ENODEV; -+ /* New driver API : try to find the handler */ -+ handler = get_handler(dev, cmd); -+ if(handler != NULL) { -+ /* Standard and private are not the same */ -+ if(cmd < SIOCIWFIRSTPRIV) -+ return ioctl_standard_call(dev, -+ ifr, -+ cmd, -+ handler); -+ else -+ return ioctl_private_call(dev, -+ ifr, -+ cmd, -+ handler); -+ } -+ /* Old driver API : call driver ioctl handler */ -+ if (dev->do_ioctl) { -+ return dev->do_ioctl(dev, ifr, cmd); -+ } -+ return -EOPNOTSUPP; -+ } -+ /* Not reached */ -+ return -EINVAL; -+} diff --git a/packages/linux/files/iw_handlers.w14-5.diff b/packages/linux/files/iw_handlers.w14-5.diff deleted file mode 100644 index 539b160068..0000000000 --- a/packages/linux/files/iw_handlers.w14-5.diff +++ /dev/null @@ -1,838 +0,0 @@ -diff -u -p -r --new-file linux/include/linux-w13/rtnetlink.h linux/include/linux/rtnetlink.h ---- linux/include/linux-w13/rtnetlink.h Thu Jun 6 14:44:08 2002 -+++ linux/include/linux/rtnetlink.h Thu Jun 6 15:47:44 2002 -@@ -440,12 +440,14 @@ enum - #define IFLA_COST IFLA_COST - IFLA_PRIORITY, - #define IFLA_PRIORITY IFLA_PRIORITY -- IFLA_MASTER -+ IFLA_MASTER, - #define IFLA_MASTER IFLA_MASTER -+ IFLA_WIRELESS, /* Wireless Extension event - see wireless.h */ -+#define IFLA_WIRELESS IFLA_WIRELESS - }; - - --#define IFLA_MAX IFLA_MASTER -+#define IFLA_MAX IFLA_WIRELESS - - #define IFLA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct ifinfomsg)))) - #define IFLA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct ifinfomsg)) -diff -u -p -r --new-file linux/include/linux-w13/wireless.h linux/include/linux/wireless.h ---- linux/include/linux-w13/wireless.h Thu Jun 6 15:00:28 2002 -+++ linux/include/linux/wireless.h Thu Jun 6 15:47:44 2002 -@@ -1,10 +1,10 @@ - /* - * This file define a set of standard wireless extensions - * -- * Version : 13 6.12.01 -+ * Version : 14 25.1.02 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. - */ - - #ifndef _LINUX_WIRELESS_H -@@ -40,7 +40,7 @@ - * # include/linux/netdevice.h (one place) - * # include/linux/proc_fs.h (one place) - * -- * New driver API (2001 -> onward) : -+ * New driver API (2002 -> onward) : - * ------------------------------- - * This file is only concerned with the user space API and common definitions. - * The new driver API is defined and documented in : -@@ -49,6 +49,11 @@ - * Note as well that /proc/net/wireless implementation has now moved in : - * # include/linux/wireless.c - * -+ * Wireless Events (2002 -> onward) : -+ * -------------------------------- -+ * Events are defined at the end of this file, and implemented in : -+ * # include/linux/wireless.c -+ * - * Other comments : - * -------------- - * Do not add here things that are redundant with other mechanisms -@@ -75,7 +80,7 @@ - * (there is some stuff that will be added in the future...) - * I just plan to increment with each new version. - */ --#define WIRELESS_EXT 13 -+#define WIRELESS_EXT 14 - - /* - * Changes : -@@ -141,6 +146,13 @@ - * - Document creation of new driver API. - * - Extract union iwreq_data from struct iwreq (for new driver API). - * - Rename SIOCSIWNAME as SIOCSIWCOMMIT -+ * -+ * V13 to V14 -+ * ---------- -+ * - Wireless Events support : define struct iw_event -+ * - Define additional specific event numbers -+ * - Add "addr" and "param" fields in union iwreq_data -+ * - AP scanning stuff (SIOCSIWSCAN and friends) - */ - - /**************************** CONSTANTS ****************************/ -@@ -175,6 +187,8 @@ - #define SIOCSIWAP 0x8B14 /* set access point MAC addresses */ - #define SIOCGIWAP 0x8B15 /* get access point MAC addresses */ - #define SIOCGIWAPLIST 0x8B17 /* get list of access point in range */ -+#define SIOCSIWSCAN 0x8B18 /* trigger scanning */ -+#define SIOCGIWSCAN 0x8B19 /* get scanning results */ - - /* 802.11 specific support */ - #define SIOCSIWESSID 0x8B1A /* set ESSID (network name) */ -@@ -238,6 +252,15 @@ - #define IW_IS_SET(cmd) (!((cmd) & 0x1)) - #define IW_IS_GET(cmd) ((cmd) & 0x1) - -+/* ----------------------- WIRELESS EVENTS ----------------------- */ -+/* Those are *NOT* ioctls, do not issue request on them !!! */ -+/* Most events use the same identifier as ioctl requests */ -+ -+#define IWEVTXDROP 0x8C00 /* Packet dropped to excessive retry */ -+#define IWEVQUAL 0x8C01 /* Quality part of statistics */ -+ -+#define IWEVFIRST 0x8C00 -+ - /* ------------------------- PRIVATE INFO ------------------------- */ - /* - * The following is used with SIOCGIWPRIV. It allow a driver to define -@@ -340,6 +363,19 @@ - #define IW_RETRY_MAX 0x0002 /* Value is a maximum */ - #define IW_RETRY_RELATIVE 0x0004 /* Value is not in seconds/ms/us */ - -+/* Scanning request flags */ -+#define IW_SCAN_DEFAULT 0x0000 /* Default scan of the driver */ -+#define IW_SCAN_ALL_ESSID 0x0001 /* Scan all ESSIDs */ -+#define IW_SCAN_THIS_ESSID 0x0002 /* Scan only this ESSID */ -+#define IW_SCAN_ALL_FREQ 0x0004 /* Scan all Frequencies */ -+#define IW_SCAN_THIS_FREQ 0x0008 /* Scan only this Frequency */ -+#define IW_SCAN_ALL_MODE 0x0010 /* Scan all Modes */ -+#define IW_SCAN_THIS_MODE 0x0020 /* Scan only this Mode */ -+#define IW_SCAN_ALL_RATE 0x0040 /* Scan all Bit-Rates */ -+#define IW_SCAN_THIS_RATE 0x0080 /* Scan only this Bit-Rate */ -+/* Maximum size of returned data */ -+#define IW_SCAN_MAX_DATA 4096 /* In bytes */ -+ - /****************************** TYPES ******************************/ - - /* --------------------------- SUBTYPES --------------------------- */ -@@ -466,9 +502,12 @@ union iwreq_data - - struct iw_point encoding; /* Encoding stuff : tokens */ - struct iw_param power; /* PM duration/timeout */ -+ struct iw_quality qual; /* Quality part of statistics */ - - struct sockaddr ap_addr; /* Access point address */ -+ struct sockaddr addr; /* Destination address (hw) */ - -+ struct iw_param param; /* Other small parameters */ - struct iw_point data; /* Other large parameters */ - }; - -@@ -595,5 +634,36 @@ struct iw_priv_args - __u16 get_args; /* Type and number of args */ - char name[IFNAMSIZ]; /* Name of the extension */ - }; -+ -+/* ----------------------- WIRELESS EVENTS ----------------------- */ -+/* -+ * Wireless events are carried through the rtnetlink socket to user -+ * space. They are encapsulated in the IFLA_WIRELESS field of -+ * a RTM_NEWLINK message. -+ */ -+ -+/* -+ * A Wireless Event. Contains basically the same data as the ioctl... -+ */ -+struct iw_event -+{ -+ __u16 len; /* Real lenght of this stuff */ -+ __u16 cmd; /* Wireless IOCTL */ -+ union iwreq_data u; /* IOCTL fixed payload */ -+}; -+ -+/* Size of the Event prefix (including padding and alignement junk) */ -+#define IW_EV_LCP_LEN (sizeof(struct iw_event) - sizeof(union iwreq_data)) -+/* Size of the various events */ -+#define IW_EV_CHAR_LEN (IW_EV_LCP_LEN + IFNAMSIZ) -+#define IW_EV_UINT_LEN (IW_EV_LCP_LEN + sizeof(__u32)) -+#define IW_EV_FREQ_LEN (IW_EV_LCP_LEN + sizeof(struct iw_freq)) -+#define IW_EV_POINT_LEN (IW_EV_LCP_LEN + sizeof(struct iw_point)) -+#define IW_EV_PARAM_LEN (IW_EV_LCP_LEN + sizeof(struct iw_param)) -+#define IW_EV_ADDR_LEN (IW_EV_LCP_LEN + sizeof(struct sockaddr)) -+#define IW_EV_QUAL_LEN (IW_EV_LCP_LEN + sizeof(struct iw_quality)) -+ -+/* Note : in the case of iw_point, the extra data will come at the -+ * end of the event */ - - #endif /* _LINUX_WIRELESS_H */ -diff -u -p -r --new-file linux/include/net-w13/iw_handler.h linux/include/net/iw_handler.h ---- linux/include/net-w13/iw_handler.h Thu Jun 6 15:06:16 2002 -+++ linux/include/net/iw_handler.h Thu Jun 6 15:48:06 2002 -@@ -1,10 +1,10 @@ - /* - * This file define the new driver API for Wireless Extensions - * -- * Version : 2 6.12.01 -+ * Version : 3 17.1.02 - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 2001 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 2001-2002 Jean Tourrilhes, All Rights Reserved. - */ - - #ifndef _IW_HANDLER_H -@@ -33,7 +33,7 @@ - * o The user space interface is tied to ioctl because of the use - * copy_to/from_user. - * -- * New driver API (2001 -> onward) : -+ * New driver API (2002 -> onward) : - * ------------------------------- - * The new driver API is just a bunch of standard functions (handlers), - * each handling a specific Wireless Extension. The driver just export -@@ -206,7 +206,18 @@ - * will be needed... - * I just plan to increment with each new version. - */ --#define IW_HANDLER_VERSION 2 -+#define IW_HANDLER_VERSION 3 -+ -+/* -+ * Changes : -+ * -+ * V2 to V3 -+ * -------- -+ * - Move event definition in <linux/wireless.h> -+ * - Add Wireless Event support : -+ * o wireless_send_event() prototype -+ * o iwe_stream_add_event/point() inline functions -+ */ - - /**************************** CONSTANTS ****************************/ - -@@ -225,6 +236,7 @@ - #define IW_HEADER_TYPE_POINT 6 /* struct iw_point */ - #define IW_HEADER_TYPE_PARAM 7 /* struct iw_param */ - #define IW_HEADER_TYPE_ADDR 8 /* struct sockaddr */ -+#define IW_HEADER_TYPE_QUAL 9 /* struct iw_quality */ - - /* Handling flags */ - /* Most are not implemented. I just use them as a reminder of some -@@ -233,7 +245,8 @@ - /* Wrapper level flags */ - #define IW_DESCR_FLAG_DUMP 0x0001 /* Not part of the dump command */ - #define IW_DESCR_FLAG_EVENT 0x0002 /* Generate an event on SET */ --#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET request is ROOT only */ -+#define IW_DESCR_FLAG_RESTRICT 0x0004 /* GET : request is ROOT only */ -+ /* SET : Omit payload from generated iwevent */ - /* Driver level flags */ - #define IW_DESCR_FLAG_WAIT 0x0100 /* Wait for driver event */ - -@@ -303,25 +316,6 @@ struct iw_handler_def - * 'struct net_device' to here, to minimise bloat. */ - }; - --/* ----------------------- WIRELESS EVENTS ----------------------- */ --/* -- * Currently we don't support events, so let's just plan for the -- * future... -- */ -- --/* -- * A Wireless Event. -- */ --// How do we define short header ? We don't want a flag on length. --// Probably a flag on event ? Highest bit to zero... --struct iw_event --{ -- __u16 length; /* Lenght of this stuff */ -- __u16 event; /* Wireless IOCTL */ -- union iwreq_data header; /* IOCTL fixed payload */ -- char extra[0]; /* Optional IOCTL data */ --}; -- - /* ---------------------- IOCTL DESCRIPTION ---------------------- */ - /* - * One of the main goal of the new interface is to deal entirely with -@@ -369,6 +363,88 @@ extern int dev_get_wireless_info(char * - extern int wireless_process_ioctl(struct ifreq *ifr, unsigned int cmd); - - /* Second : functions that may be called by driver modules */ --/* None yet */ - --#endif /* _LINUX_WIRELESS_H */ -+/* Send a single event to user space */ -+extern void wireless_send_event(struct net_device * dev, -+ unsigned int cmd, -+ union iwreq_data * wrqu, -+ char * extra); -+ -+/* We may need a function to send a stream of events to user space. -+ * More on that later... */ -+ -+/************************* INLINE FUNTIONS *************************/ -+/* -+ * Function that are so simple that it's more efficient inlining them -+ */ -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Wrapper to add an Wireless Event to a stream of events. -+ */ -+static inline char * -+iwe_stream_add_event(char * stream, /* Stream of events */ -+ char * ends, /* End of stream */ -+ struct iw_event *iwe, /* Payload */ -+ int event_len) /* Real size of payload */ -+{ -+ /* Check if it's possible */ -+ if((stream + event_len) < ends) { -+ iwe->len = event_len; -+ memcpy(stream, (char *) iwe, event_len); -+ stream += event_len; -+ } -+ return stream; -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Wrapper to add an short Wireless Event containing a pointer to a -+ * stream of events. -+ */ -+static inline char * -+iwe_stream_add_point(char * stream, /* Stream of events */ -+ char * ends, /* End of stream */ -+ struct iw_event *iwe, /* Payload */ -+ char * extra) -+{ -+ int event_len = IW_EV_POINT_LEN + iwe->u.data.length; -+ /* Check if it's possible */ -+ if((stream + event_len) < ends) { -+ iwe->len = event_len; -+ memcpy(stream, (char *) iwe, IW_EV_POINT_LEN); -+ memcpy(stream + IW_EV_POINT_LEN, extra, iwe->u.data.length); -+ stream += event_len; -+ } -+ return stream; -+} -+ -+/*------------------------------------------------------------------*/ -+/* -+ * Wrapper to add a value to a Wireless Event in a stream of events. -+ * Be careful, this one is tricky to use properly : -+ * At the first run, you need to have (value = event + IW_EV_LCP_LEN). -+ */ -+static inline char * -+iwe_stream_add_value(char * event, /* Event in the stream */ -+ char * value, /* Value in event */ -+ char * ends, /* End of stream */ -+ struct iw_event *iwe, /* Payload */ -+ int event_len) /* Real size of payload */ -+{ -+ /* Don't duplicate LCP */ -+ event_len -= IW_EV_LCP_LEN; -+ -+ /* Check if it's possible */ -+ if((value + event_len) < ends) { -+ /* Add new value */ -+ memcpy(value, (char *) iwe + IW_EV_LCP_LEN, event_len); -+ value += event_len; -+ /* Patch LCP */ -+ iwe->len = value - event; -+ memcpy(event, (char *) iwe, IW_EV_LCP_LEN); -+ } -+ return value; -+} -+ -+#endif /* _IW_HANDLER_H */ -diff -u -p -r --new-file linux/net/netsyms-w13.c linux/net/netsyms.c ---- linux/net/netsyms-w13.c Thu Jun 6 15:46:34 2002 -+++ linux/net/netsyms.c Thu Jun 6 15:47:44 2002 -@@ -588,4 +588,10 @@ EXPORT_SYMBOL(register_gifconf); - EXPORT_SYMBOL(net_call_rx_atomic); - EXPORT_SYMBOL(softnet_data); - -+#if defined(CONFIG_NET_RADIO) || defined(CONFIG_NET_PCMCIA_RADIO) -+/* Don't include the whole header mess for a single function */ -+extern void wireless_send_event(struct net_device *dev, unsigned int cmd, union iwreq_data *wrqu, char *extra); -+EXPORT_SYMBOL(wireless_send_event); -+#endif /* CONFIG_NET_RADIO || CONFIG_NET_PCMCIA_RADIO */ -+ - #endif /* CONFIG_NET */ -diff -u -p -r --new-file linux/net/core/wireless-w13.c linux/net/core/wireless.c ---- linux/net/core/wireless-w13.c Thu Jun 6 15:46:45 2002 -+++ linux/net/core/wireless.c Thu Jun 6 15:48:06 2002 -@@ -2,7 +2,7 @@ - * This file implement the Wireless Extensions APIs. - * - * Authors : Jean Tourrilhes - HPL - <jt@hpl.hp.com> -- * Copyright (c) 1997-2001 Jean Tourrilhes, All Rights Reserved. -+ * Copyright (c) 1997-2002 Jean Tourrilhes, All Rights Reserved. - * - * (As all part of the Linux kernel, this file is GPL) - */ -@@ -25,6 +25,16 @@ - * o Added iw_handler handling ;-) - * o Added standard ioctl description - * o Initial dumb commit strategy based on orinoco.c -+ * -+ * v3 - 19.12.01 - Jean II -+ * o Make sure we don't go out of standard_ioctl[] in ioctl_standard_call -+ * o Add event dispatcher function -+ * o Add event description -+ * o Propagate events as rtnetlink IFLA_WIRELESS option -+ * o Generate event on selected SET requests -+ * -+ * v4 - 18.04.01 - Jean II -+ * o Fix stupid off by one in iw_ioctl_description : IW_ESSID_MAX_SIZE + 1 - */ - - /***************************** INCLUDES *****************************/ -@@ -33,6 +43,7 @@ - #include <linux/config.h> /* Not needed ??? */ - #include <linux/types.h> /* off_t */ - #include <linux/netdevice.h> /* struct ifreq, dev_get_by_name() */ -+#include <linux/rtnetlink.h> /* rtnetlink stuff */ - - #include <linux/wireless.h> /* Pretty obvious */ - #include <net/iw_handler.h> /* New driver API */ -@@ -44,14 +55,23 @@ - - /* Debuging stuff */ - #undef WE_IOCTL_DEBUG /* Debug IOCTL API */ -+#undef WE_EVENT_DEBUG /* Debug Event dispatcher */ -+ -+/* Options */ -+#define WE_EVENT_NETLINK /* Propagate events using rtnetlink */ -+#define WE_SET_EVENT /* Generate an event on some set commands */ - - /************************* GLOBAL VARIABLES *************************/ - /* - * You should not use global variables, because or re-entrancy. - * On our case, it's only const, so it's OK... - */ -+/* -+ * Meta-data about all the standard Wireless Extension request we -+ * know about. -+ */ - static const struct iw_ioctl_description standard_ioctl[] = { -- /* SIOCSIWCOMMIT (internal) */ -+ /* SIOCSIWCOMMIT */ - { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, - /* SIOCGIWNAME */ - { IW_HEADER_TYPE_CHAR, 0, 0, 0, 0, IW_DESCR_FLAG_DUMP}, -@@ -99,18 +119,18 @@ static const struct iw_ioctl_description - { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, - /* SIOCGIWAPLIST */ - { IW_HEADER_TYPE_POINT, 0, (sizeof(struct sockaddr) + sizeof(struct iw_quality)), 0, IW_MAX_AP, 0}, -- /* -- hole -- */ -- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -- /* -- hole -- */ -- { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, -+ /* SIOCSIWSCAN */ -+ { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, -+ /* SIOCGIWSCAN */ -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_SCAN_MAX_DATA, 0}, - /* SIOCSIWESSID */ -- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_EVENT}, -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_EVENT}, - /* SIOCGIWESSID */ -- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, IW_DESCR_FLAG_DUMP}, -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, IW_DESCR_FLAG_DUMP}, - /* SIOCSIWNICKN */ -- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 0}, - /* SIOCGIWNICKN */ -- { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE, 0}, -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ESSID_MAX_SIZE + 1, 0}, - /* -- hole -- */ - { IW_HEADER_TYPE_NULL, 0, 0, 0, 0, 0}, - /* -- hole -- */ -@@ -136,7 +156,7 @@ static const struct iw_ioctl_description - /* SIOCGIWRETRY */ - { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, - /* SIOCSIWENCODE */ -- { IW_HEADER_TYPE_POINT, 4, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT}, -+ { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_EVENT | IW_DESCR_FLAG_RESTRICT}, - /* SIOCGIWENCODE */ - { IW_HEADER_TYPE_POINT, 0, 1, 0, IW_ENCODING_TOKEN_MAX, IW_DESCR_FLAG_DUMP | IW_DESCR_FLAG_RESTRICT}, - /* SIOCSIWPOWER */ -@@ -144,9 +164,38 @@ static const struct iw_ioctl_description - /* SIOCGIWPOWER */ - { IW_HEADER_TYPE_PARAM, 0, 0, 0, 0, 0}, - }; -+static const int standard_ioctl_num = (sizeof(standard_ioctl) / -+ sizeof(struct iw_ioctl_description)); -+ -+/* -+ * Meta-data about all the additional standard Wireless Extension events -+ * we know about. -+ */ -+static const struct iw_ioctl_description standard_event[] = { -+ /* IWEVTXDROP */ -+ { IW_HEADER_TYPE_ADDR, 0, 0, 0, 0, 0}, -+ /* IWEVQUAL */ -+ { IW_HEADER_TYPE_QUAL, 0, 0, 0, 0, 0}, -+}; -+static const int standard_event_num = (sizeof(standard_event) / -+ sizeof(struct iw_ioctl_description)); - - /* Size (in bytes) of the various private data types */ --char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; -+static const char priv_type_size[] = { 0, 1, 1, 0, 4, 4, 0, 0 }; -+ -+/* Size (in bytes) of various events */ -+static const int event_type_size[] = { -+ IW_EV_LCP_LEN, -+ 0, -+ IW_EV_CHAR_LEN, -+ 0, -+ IW_EV_UINT_LEN, -+ IW_EV_FREQ_LEN, -+ IW_EV_POINT_LEN, /* Without variable payload */ -+ IW_EV_PARAM_LEN, -+ IW_EV_ADDR_LEN, -+ IW_EV_QUAL_LEN, -+}; - - /************************ COMMON SUBROUTINES ************************/ - /* -@@ -162,7 +211,8 @@ char priv_type_size[] = { 0, 1, 1, 0, 4, - static inline iw_handler get_handler(struct net_device *dev, - unsigned int cmd) - { -- unsigned int index; /* MUST be unsigned */ -+ /* Don't "optimise" the following variable, it will crash */ -+ unsigned int index; /* *MUST* be unsigned */ - - /* Check if we have some wireless handlers defined */ - if(dev->wireless_handlers == NULL) -@@ -269,9 +319,9 @@ static inline int sprintf_wireless_stats - stats->status, - stats->qual.qual, - stats->qual.updated & 1 ? '.' : ' ', -- stats->qual.level, -+ ((__u8) stats->qual.level), - stats->qual.updated & 2 ? '.' : ' ', -- stats->qual.noise, -+ ((__u8) stats->qual.noise), - stats->qual.updated & 4 ? '.' : ' ', - stats->discard.nwid, - stats->discard.code, -@@ -423,12 +473,14 @@ static inline int ioctl_standard_call(st - int ret = -EINVAL; - - /* Get the description of the IOCTL */ -+ if((cmd - SIOCIWFIRST) >= standard_ioctl_num) -+ return -EOPNOTSUPP; - descr = &(standard_ioctl[cmd - SIOCIWFIRST]); - - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "%s : Found standard handler for 0x%04X\n", -+ printk(KERN_DEBUG "%s (WE) : Found standard handler for 0x%04X\n", - ifr->ifr_name, cmd); -- printk(KERN_DEBUG "Header type : %d, token type : %d, token_size : %d, max_token : %d\n", descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -+ printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); - #endif /* WE_IOCTL_DEBUG */ - - /* Prepare the call */ -@@ -437,8 +489,16 @@ static inline int ioctl_standard_call(st - - /* Check if we have a pointer to user space data or not */ - if(descr->header_type != IW_HEADER_TYPE_POINT) { -+ - /* No extra arguments. Trivial to handle */ - ret = handler(dev, &info, &(iwr->u), NULL); -+ -+#ifdef WE_SET_EVENT -+ /* Generate an event to notify listeners of the change */ -+ if((descr->flags & IW_DESCR_FLAG_EVENT) && -+ ((ret == 0) || (ret == -EIWCOMMIT))) -+ wireless_send_event(dev, cmd, &(iwr->u), NULL); -+#endif /* WE_SET_EVENT */ - } else { - char * extra; - int err; -@@ -466,8 +526,8 @@ static inline int ioctl_standard_call(st - } - - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "Malloc %d bytes\n", -- descr->max_tokens * descr->token_size); -+ printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", -+ dev->name, descr->max_tokens * descr->token_size); - #endif /* WE_IOCTL_DEBUG */ - - /* Always allocate for max space. Easier, and won't last -@@ -488,7 +548,8 @@ static inline int ioctl_standard_call(st - return -EFAULT; - } - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "Got %d bytes\n", -+ printk(KERN_DEBUG "%s (WE) : Got %d bytes\n", -+ dev->name, - iwr->u.data.length * descr->token_size); - #endif /* WE_IOCTL_DEBUG */ - } -@@ -504,11 +565,26 @@ static inline int ioctl_standard_call(st - if (err) - ret = -EFAULT; - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "Wrote %d bytes\n", -+ printk(KERN_DEBUG "%s (WE) : Wrote %d bytes\n", -+ dev->name, - iwr->u.data.length * descr->token_size); - #endif /* WE_IOCTL_DEBUG */ - } - -+#ifdef WE_SET_EVENT -+ /* Generate an event to notify listeners of the change */ -+ if((descr->flags & IW_DESCR_FLAG_EVENT) && -+ ((ret == 0) || (ret == -EIWCOMMIT))) { -+ if(descr->flags & IW_DESCR_FLAG_RESTRICT) -+ /* If the event is restricted, don't -+ * export the payload */ -+ wireless_send_event(dev, cmd, &(iwr->u), NULL); -+ else -+ wireless_send_event(dev, cmd, &(iwr->u), -+ extra); -+ } -+#endif /* WE_SET_EVENT */ -+ - /* Cleanup - I told you it wasn't that long ;-) */ - kfree(extra); - } -@@ -558,11 +634,12 @@ static inline int ioctl_private_call(str - } - - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "%s : Found private handler for 0x%04X\n", -+ printk(KERN_DEBUG "%s (WE) : Found private handler for 0x%04X\n", - ifr->ifr_name, cmd); - if(descr) { -- printk(KERN_DEBUG "Name %s, set %X, get %X\n", -- descr->name, descr->set_args, descr->get_args); -+ printk(KERN_DEBUG "%s (WE) : Name %s, set %X, get %X\n", -+ dev->name, descr->name, -+ descr->set_args, descr->get_args); - } - #endif /* WE_IOCTL_DEBUG */ - -@@ -617,7 +694,8 @@ static inline int ioctl_private_call(str - } - - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "Malloc %d bytes\n", extra_size); -+ printk(KERN_DEBUG "%s (WE) : Malloc %d bytes\n", -+ dev->name, extra_size); - #endif /* WE_IOCTL_DEBUG */ - - /* Always allocate for max space. Easier, and won't last -@@ -636,7 +714,8 @@ static inline int ioctl_private_call(str - return -EFAULT; - } - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "Got %d elem\n", iwr->u.data.length); -+ printk(KERN_DEBUG "%s (WE) : Got %d elem\n", -+ dev->name, iwr->u.data.length); - #endif /* WE_IOCTL_DEBUG */ - } - -@@ -650,8 +729,8 @@ static inline int ioctl_private_call(str - if (err) - ret = -EFAULT; - #ifdef WE_IOCTL_DEBUG -- printk(KERN_DEBUG "Wrote %d elem\n", -- iwr->u.data.length); -+ printk(KERN_DEBUG "%s (WE) : Wrote %d elem\n", -+ dev->name, iwr->u.data.length); - #endif /* WE_IOCTL_DEBUG */ - } - -@@ -730,4 +809,178 @@ int wireless_process_ioctl(struct ifreq - } - /* Not reached */ - return -EINVAL; -+} -+ -+/************************* EVENT PROCESSING *************************/ -+/* -+ * Process events generated by the wireless layer or the driver. -+ * Most often, the event will be propagated through rtnetlink -+ */ -+ -+#ifdef WE_EVENT_NETLINK -+/* "rtnl" is defined in net/core/rtnetlink.c, but we need it here. -+ * It is declared in <linux/rtnetlink.h> */ -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Fill a rtnetlink message with our event data. -+ * Note that we propage only the specified event and don't dump the -+ * current wireless config. Dumping the wireless config is far too -+ * expensive (for each parameter, the driver need to query the hardware). -+ */ -+static inline int rtnetlink_fill_iwinfo(struct sk_buff * skb, -+ struct net_device * dev, -+ int type, -+ char * event, -+ int event_len) -+{ -+ struct ifinfomsg *r; -+ struct nlmsghdr *nlh; -+ unsigned char *b = skb->tail; -+ -+ nlh = NLMSG_PUT(skb, 0, 0, type, sizeof(*r)); -+ r = NLMSG_DATA(nlh); -+ r->ifi_family = AF_UNSPEC; -+ r->ifi_type = dev->type; -+ r->ifi_index = dev->ifindex; -+ r->ifi_flags = dev->flags; -+ r->ifi_change = 0; /* Wireless changes don't affect those flags */ -+ -+ /* Add the wireless events in the netlink packet */ -+ RTA_PUT(skb, IFLA_WIRELESS, -+ event_len, event); -+ -+ nlh->nlmsg_len = skb->tail - b; -+ return skb->len; -+ -+nlmsg_failure: -+rtattr_failure: -+ skb_trim(skb, b - skb->data); -+ return -1; -+} -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Create and broadcast and send it on the standard rtnetlink socket -+ * This is a pure clone rtmsg_ifinfo() in net/core/rtnetlink.c -+ * Andrzej Krzysztofowicz mandated that I used a IFLA_XXX field -+ * within a RTM_NEWLINK event. -+ */ -+static inline void rtmsg_iwinfo(struct net_device * dev, -+ char * event, -+ int event_len) -+{ -+ struct sk_buff *skb; -+ int size = NLMSG_GOODSIZE; -+ -+ skb = alloc_skb(size, GFP_ATOMIC); -+ if (!skb) -+ return; -+ -+ if (rtnetlink_fill_iwinfo(skb, dev, RTM_NEWLINK, -+ event, event_len) < 0) { -+ kfree_skb(skb); -+ return; -+ } -+ NETLINK_CB(skb).dst_groups = RTMGRP_LINK; -+ netlink_broadcast(rtnl, skb, 0, RTMGRP_LINK, GFP_ATOMIC); -+} -+#endif /* WE_EVENT_NETLINK */ -+ -+/* ---------------------------------------------------------------- */ -+/* -+ * Main event dispatcher. Called from other parts and drivers. -+ * Send the event on the apropriate channels. -+ * May be called from interrupt context. -+ */ -+void wireless_send_event(struct net_device * dev, -+ unsigned int cmd, -+ union iwreq_data * wrqu, -+ char * extra) -+{ -+ const struct iw_ioctl_description * descr = NULL; -+ int extra_len = 0; -+ struct iw_event *event; /* Mallocated whole event */ -+ int event_len; /* Its size */ -+ int hdr_len; /* Size of the event header */ -+ /* Don't "optimise" the following variable, it will crash */ -+ unsigned cmd_index; /* *MUST* be unsigned */ -+ -+ /* Get the description of the IOCTL */ -+ if(cmd <= SIOCIWLAST) { -+ cmd_index = cmd - SIOCIWFIRST; -+ if(cmd_index < standard_ioctl_num) -+ descr = &(standard_ioctl[cmd_index]); -+ } else { -+ cmd_index = cmd - IWEVFIRST; -+ if(cmd_index < standard_event_num) -+ descr = &(standard_event[cmd_index]); -+ } -+ /* Don't accept unknown events */ -+ if(descr == NULL) { -+ /* Note : we don't return an error to the driver, because -+ * the driver would not know what to do about it. It can't -+ * return an error to the user, because the event is not -+ * initiated by a user request. -+ * The best the driver could do is to log an error message. -+ * We will do it ourselves instead... -+ */ -+ printk(KERN_ERR "%s (WE) : Invalid Wireless Event (0x%04X)\n", -+ dev->name, cmd); -+ return; -+ } -+#ifdef WE_EVENT_DEBUG -+ printk(KERN_DEBUG "%s (WE) : Got event 0x%04X\n", -+ dev->name, cmd); -+ printk(KERN_DEBUG "%s (WE) : Header type : %d, Token type : %d, size : %d, token : %d\n", dev->name, descr->header_type, descr->token_type, descr->token_size, descr->max_tokens); -+#endif /* WE_EVENT_DEBUG */ -+ -+ /* Check extra parameters and set extra_len */ -+ if(descr->header_type == IW_HEADER_TYPE_POINT) { -+ /* Check if number of token fits within bounds */ -+ if(wrqu->data.length > descr->max_tokens) { -+ printk(KERN_ERR "%s (WE) : Wireless Event too big (%d)\n", dev->name, wrqu->data.length); -+ return; -+ } -+ if(wrqu->data.length < descr->min_tokens) { -+ printk(KERN_ERR "%s (WE) : Wireless Event too small (%d)\n", dev->name, wrqu->data.length); -+ return; -+ } -+ /* Calculate extra_len - extra is NULL for restricted events */ -+ if(extra != NULL) -+ extra_len = wrqu->data.length * descr->token_size; -+#ifdef WE_EVENT_DEBUG -+ printk(KERN_DEBUG "%s (WE) : Event 0x%04X, tokens %d, extra_len %d\n", dev->name, cmd, wrqu->data.length, extra_len); -+#endif /* WE_EVENT_DEBUG */ -+ } -+ -+ /* Total length of the event */ -+ hdr_len = event_type_size[descr->header_type]; -+ event_len = hdr_len + extra_len; -+ -+#ifdef WE_EVENT_DEBUG -+ printk(KERN_DEBUG "%s (WE) : Event 0x%04X, hdr_len %d, event_len %d\n", dev->name, cmd, hdr_len, event_len); -+#endif /* WE_EVENT_DEBUG */ -+ -+ /* Create temporary buffer to hold the event */ -+ event = kmalloc(event_len, GFP_ATOMIC); -+ if(event == NULL) -+ return; -+ -+ /* Fill event */ -+ event->len = event_len; -+ event->cmd = cmd; -+ memcpy(&event->u, wrqu, hdr_len - IW_EV_LCP_LEN); -+ if(extra != NULL) -+ memcpy(((char *) event) + hdr_len, extra, extra_len); -+ -+#ifdef WE_EVENT_NETLINK -+ /* rtnetlink event channel */ -+ rtmsg_iwinfo(dev, (char *) event, event_len); -+#endif /* WE_EVENT_NETLINK */ -+ -+ /* Cleanup */ -+ kfree(event); -+ -+ return; /* Always success, I guess ;-) */ - } diff --git a/packages/linux/files/linux-2.4-cpufreq.patch b/packages/linux/files/linux-2.4-cpufreq.patch deleted file mode 100644 index c3526bb30d..0000000000 --- a/packages/linux/files/linux-2.4-cpufreq.patch +++ /dev/null @@ -1,20 +0,0 @@ -Index: include/linux/cpufreq.h -=================================================================== -RCS file: /cvs/linux/kernel/include/linux/cpufreq.h,v -retrieving revision 1.4 -diff -u -r1.4 cpufreq.h ---- linux/include/linux/cpufreq.h 23 Aug 2002 22:18:47 -0000 1.4 -+++ linux/include/linux/cpufreq.h 29 Apr 2004 08:44:18 -0000 -@@ -16,9 +16,9 @@ - #include <linux/notifier.h> - - #ifndef CONFIG_SMP --#define cpufreq_current(cpu) ((void)(cpu), __cpufreq_cur) --#define cpufreq_max(cpu) ((void)(cpu), __cpufreq_max) --#define cpufreq_min(cpu) ((void)(cpu), __cpufreq_min) -+#define cpufreq_current(cpu) (__cpufreq_cur) -+#define cpufreq_max(cpu) (__cpufreq_max) -+#define cpufreq_min(cpu) (__cpufreq_min) - #else - /* - * Should be something like: diff --git a/packages/linux/files/linux-2.4-mmc-debugging.patch b/packages/linux/files/linux-2.4-mmc-debugging.patch deleted file mode 100644 index 6cde9e8ed2..0000000000 --- a/packages/linux/files/linux-2.4-mmc-debugging.patch +++ /dev/null @@ -1,15 +0,0 @@ ---- kernel/include/linux/mmc/mmc_protocol.h 2005-04-09 17:30:57.930462521 +0200 -+++ /tmp/mmc_protocol.h 2005-04-09 17:30:33.649097537 +0200 -@@ -273,10 +273,10 @@ - #define START_MMC_DEBUG(n) do { if (n <= g_mmc_debug) - #define END_MMC_DEBUG } while (0) - #else - #define MMC_DEBUG(n, args...) --#define START_MMC_DEBUG(n) --#define END_MMC_DEBUG -+#define START_MMC_DEBUG(n) do { if (0) -+#define END_MMC_DEBUG } while (0) - #endif /* CONFIG_MMC_DEBUG */ - - #endif /* MMC_MMC_PROTOCOL_H */ - diff --git a/packages/linux/files/linux-2.4-no-short-loads.patch b/packages/linux/files/linux-2.4-no-short-loads.patch deleted file mode 100644 index f2d6c74224..0000000000 --- a/packages/linux/files/linux-2.4-no-short-loads.patch +++ /dev/null @@ -1,18 +0,0 @@ -Index: arch/arm/Makefile -=================================================================== -RCS file: /cvs/linux/kernel/arch/arm/Makefile,v -retrieving revision 1.47 -diff -u -r1.47 Makefile ---- linux/arch/arm/Makefile 9 Jul 2003 14:10:56 -0000 1.47 -+++ linux/arch/arm/Makefile 28 Apr 2004 21:11:04 -0000 -@@ -60,8 +60,8 @@ - tune-$(CONFIG_CPU_XSCALE) :=-mtune=xscale - #tune-$(CONFIG_CPU_XSCALE) :=-mtune=strongarm - --CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm --CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -mshort-load-bytes -msoft-float -Uarm -+CFLAGS_BOOT :=$(apcs-y) $(arch-y) $(tune-y) -msoft-float -Uarm -+CFLAGS +=$(apcs-y) $(arch-y) $(tune-y) -msoft-float -Uarm - AFLAGS +=$(apcs-y) $(arch-y) -msoft-float - - ifeq ($(CONFIG_CPU_26),y) diff --git a/packages/linux/files/linux-2.4-usb-gadget.patch b/packages/linux/files/linux-2.4-usb-gadget.patch deleted file mode 100644 index 0864ee98f5..0000000000 --- a/packages/linux/files/linux-2.4-usb-gadget.patch +++ /dev/null @@ -1,29506 +0,0 @@ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/Documentation/Configure.help kernel/Documentation/Configure.help ---- /tmp/kernel/Documentation/Configure.help 2005-04-22 17:52:12.265476882 +0200 -+++ kernel/Documentation/Configure.help 2005-04-22 17:57:15.940717930 +0200 -@@ -23701,6 +23701,163 @@ - brave people. System crashes and other bad things are likely to occur if - you use this driver. If in doubt, select N. - -+CONFIG_USB_GADGET -+ USB is a master/slave protocol, organized with one master -+ host (such as a PC) controlling up to 127 peripheral devices. -+ The USB hardware is asymmetric, which makes it easier to set up: -+ you can't connect two "to-the-host" connectors to each other. -+ -+ Linux can run in the host, or in the peripheral. In both cases -+ you need a low level bus controller driver, and some software -+ talking to it. Peripheral controllers are often discrete silicon, -+ or are integrated with the CPU in a microcontroller. The more -+ familiar host side controllers have names like like "EHCI", "OHCI", -+ or "UHCI", and are usually integrated into southbridges on PC -+ motherboards. -+ -+ Enable this configuration option if you want to run Linux inside -+ a USB peripheral device. Configure one hardware driver for your -+ peripheral/device side bus controller, and a "gadget driver" for -+ your peripheral protocol. (If you use modular gadget drivers, -+ you may configure more than one.) -+ -+ If in doubt, say "N" and don't enable these drivers; most people -+ don't have this kind of hardware (except maybe inside Linux PDAs). -+ -+CONFIG_USB_GADGET_NET2280 -+ NetChip 2280 is a PCI based USB peripheral controller which -+ supports both full and high speed USB 2.0 data transfers. -+ -+ It has six configurable endpoints, as well as endpoint zero -+ (for control transfers) and several endpoints with dedicated -+ functions. -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module called "net2280" and force all -+ gadget drivers to also be dynamically linked. -+ -+CONFIG_USB_GADGET_GOKU -+ The Toshiba TC86C001 is a PCI device which includes controllers -+ for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI). -+ -+ The device controller has three configurable (bulk or interrupt) -+ endpoints, plus endpoint zero (for control transfers). -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module called "goku_udc" and force all -+ gadget drivers to also be dynamically linked. -+ -+CONFIG_USB_GADGET_PXA2XX -+ Intel's PXA 2xx series XScale ARM-5TE processors include -+ an integrated full speed USB 1.1 device controller. -+ -+ It has fifteen fixed-function endpoints, as well as endpoint -+ zero (for control transfers). -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module called "pxa2xx_udc" and force all -+ gadget drivers to also be dynamically linked. -+ -+CONFIG_USB_GADGET_SUPERH -+ Some Renesas SuperH processors (SH7705, SH7727...) include an -+ integrated high speed USB 1.1 device controller. -+ -+ It has three fixed-function endpoints, as well as endpoint zero (for -+ control transfers). -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module called "superh_udc" and force all -+ gadget drivers to also be dynamically linked. -+ -+CONFIG_USB_ZERO -+ Gadget Zero is a two-configuration device. It either sinks and -+ sources bulk data; or it loops back a configurable number of -+ transfers. It also implements control requests, for "chapter 9" -+ conformance. The driver needs only two bulk-capable endpoints, so -+ it can work on top of most device-side usb controllers. It's -+ useful for testing, and is also a working example showing how -+ USB "gadget drivers" can be written. -+ -+ Make this be the first driver you try using on top of any new -+ USB peripheral controller driver. Then you can use host-side -+ test software, like the "usbtest" driver, to put your hardware -+ and its driver through a basic set of functional tests. -+ -+ Gadget Zero also works with the host-side "usb-skeleton" driver, -+ and with many kinds of host-side test software. You may need -+ to tweak product and vendor IDs before host software knows about -+ this device, and arrange to select an appropriate configuration. -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module called "g_zero". -+ -+CONFIG_USB_ETH -+ This driver implements Ethernet style communication, in either -+ of two ways: -+ -+ - The "Communication Device Class" (CDC) Ethernet Control Model. -+ That protocol is often avoided with pure Ethernet adapters, in -+ favor of simpler vendor-specific hardware, but is widely -+ supported by firmware for smart network devices. -+ -+ - On hardware can't implement that protocol, a simpler approach -+ is used, placing fewer demands on USB. -+ -+ Within the USB device, this gadget driver exposes a network device -+ "usbX", where X depends on what other networking devices you have. -+ Treat it like a two-node Ethernet link: host, and gadget. -+ -+ The Linux-USB host-side "usbnet" driver interoperates with this -+ driver, so that deep I/O queues can be supported. On 2.4 kernels, -+ use "CDCEther" instead, if you're using the CDC option. That CDC -+ mode should also interoperate with standard CDC Ethernet class -+ drivers on other host operating systems. -+ -+ Say "y" to link the driver statically, or "m" to build a -+ dynamically linked module called "g_ether". -+ -+CONFIG_USB_ETH_RNDIS -+ Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, -+ and Microsoft provides redistributable binary RNDIS drivers for -+ older versions of Windows. -+ -+ If you say "y" here, the Ethernet gadget driver will try to provide -+ a second device configuration, supporting RNDIS to talk to such -+ Microsoft USB hosts. -+ -+CONFIG_USB_FILE_STORAGE -+ The File-backed Storage Gadget acts as a USB Mass Storage -+ disk drive. As its storage repository it can use a regular -+ file or a block device (in much the same way as the "loop" -+ device driver), specified as a module parameter. -+ -+CONFIG_USB_FILE_STORAGE_TEST -+ Say "y" to generate the larger testing version of the -+ File-backed Storage Gadget, useful for probing the -+ behavior of USB Mass Storage hosts. Not needed for -+ normal operation. -+ -+CONFIG_USB_ETH_RNDIS -+ Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol, -+ and Microsoft provides redistributable binary RNDIS drivers for -+ older versions of Windows. -+ -+ If you say "y" here, the Ethernet gadget driver will try to provide -+ a second device configuration, supporting RNDIS to talk to such -+ Microsoft USB hosts. -+ -+CONFIG_USB_FILE_STORAGE -+ The File-backed Storage Gadget acts as a USB Mass Storage -+ disk drive. As its storage repository it can use a regular -+ file or a block device (in much the same way as the "loop" -+ device driver), specified as a module parameter. -+ -+CONFIG_USB_FILE_STORAGE_TEST -+ Say "y" to generate the larger testing version of the -+ File-backed Storage Gadget, useful for probing the -+ behavior of USB Mass Storage hosts. Not needed for -+ normal operation. -+ - Winbond W83977AF IrDA Device Driver - CONFIG_WINBOND_FIR - Say Y here if you want to build IrDA support for the Winbond -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/Makefile kernel/Makefile ---- /tmp/kernel/Makefile 2005-04-22 17:52:12.362461090 +0200 -+++ kernel/Makefile 2005-04-22 17:53:19.374549284 +0200 -@@ -196,6 +196,7 @@ - DRIVERS-$(CONFIG_HAMRADIO) += drivers/net/hamradio/hamradio.o - DRIVERS-$(CONFIG_TC) += drivers/tc/tc.a - DRIVERS-$(CONFIG_USB) += drivers/usb/usbdrv.o -+DRIVERS-$(CONFIG_USB_GADGET) += drivers/usb/gadget/built-in.o - DRIVERS-$(CONFIG_LAB) += drivers/bootldr/labmod.o - DRIVERS-$(CONFIG_INPUT) += drivers/input/inputdrv.o - DRIVERS-$(CONFIG_I2O) += drivers/message/i2o/i2o.o -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/Makefile kernel/drivers/Makefile ---- /tmp/kernel/drivers/Makefile 2005-04-22 17:52:12.728401503 +0200 -+++ kernel/drivers/Makefile 2005-04-22 17:53:19.523525026 +0200 -@@ -27,6 +27,7 @@ - subdir-$(CONFIG_MAC) += macintosh - subdir-$(CONFIG_PPC) += macintosh - subdir-$(CONFIG_USB) += usb -+subdir-$(CONFIG_USB_GADGET) += usb/gadget - subdir-$(CONFIG_INPUT) += input - subdir-$(CONFIG_PHONE) += telephony - subdir-$(CONFIG_SGI) += sgi -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/Config.in kernel/drivers/usb/Config.in ---- /tmp/kernel/drivers/usb/Config.in 2005-04-22 17:52:20.663109467 +0200 -+++ kernel/drivers/usb/Config.in 2005-04-22 17:53:19.376548959 +0200 -@@ -120,4 +120,7 @@ - dep_tristate ' USB Auerswald ISDN support (EXPERIMENTAL)' CONFIG_USB_AUERSWALD $CONFIG_USB $CONFIG_EXPERIMENTAL - dep_tristate ' Tieman Voyager USB Braille display support (EXPERIMENTAL)' CONFIG_USB_BRLVGER $CONFIG_USB $CONFIG_EXPERIMENTAL - fi -+ -+source drivers/usb/gadget/Config.in -+ - endmenu -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/Config.in kernel/drivers/usb/gadget/Config.in ---- /tmp/kernel/drivers/usb/gadget/Config.in 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/Config.in 2005-04-22 17:53:19.403544563 +0200 -@@ -0,0 +1,128 @@ -+# -+# USB device-side configuration -+# for 2.4 kbuild, drivers/usb/gadget/Config.in -+# -+# Long term, this likely doesn't all belong in one directory -+# Plan to split it up eventually. -+# -+mainmenu_option next_comment -+comment 'Support for USB gadgets' -+ -+tristate 'Support for USB Gadgets' CONFIG_USB_GADGET -+if [ "$CONFIG_USB_GADGET" = "y" -o "$CONFIG_USB_GADGET" = "m" ]; then -+ -+ # -+ # really want _exactly one_ device controller driver at a time, -+ # since they control compile options for gadget drivers. -+ # -+ choice 'USB Peripheral Controller Driver' "\ -+ Intel-PXA2xx/IXP4xx CONFIG_USB_GADGET_PXA2XX \ -+ National-N9603/N9604 CONFIG_USB_GADGET_N9604 \ -+ NetChip-2280 CONFIG_USB_GADGET_NET2280 \ -+ Renesas-SH7705/7727 CONFIG_USB_GADGET_SUPERH \ -+ Toshiba-TC86C001(Goku-S) CONFIG_USB_GADGET_GOKU \ -+ " NetChip-2280 -+ -+ define_tristate CONFIG_USB_GADGET_CONTROLLER n -+ -+ if [ "$CONFIG_ARCH_PXA" = "y" -o "$CONFIG_ARCH_IXP425" = "y" ] ; then -+ if [ "$CONFIG_USB_GADGET_PXA2XX" = "y" ] ; then -+ define_tristate CONFIG_USB_PXA2XX $CONFIG_USB_GADGET -+ define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_PXA2XX -+ fi -+ fi -+ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_USB_GADGET_NET2280" = "y" ] ; then -+ define_tristate CONFIG_USB_NET2280 $CONFIG_USB_GADGET -+ define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_NET2280 -+ fi -+ if [ "$CONFIG_SUPERH" = "y" -a "$CONFIG_USB_GADGET_SUPERH" = "y" ] ; then -+ define_tristate CONFIG_USB_SUPERH $CONFIG_USB_GADGET -+ define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_SUPERH -+ fi -+ if [ "$CONFIG_PCI" = "y" -a "$CONFIG_USB_GADGET_GOKU" = "y" ] ; then -+ define_tristate CONFIG_USB_GOKU $CONFIG_USB_GADGET -+ define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_GOKU -+ fi -+ if [ "$CONFIG_USB_GADGET_N9604" = "y" ] ; then -+ define_tristate CONFIG_USB_N9604 $CONFIG_USB_GADGET -+ define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_N9604 -+ fi -+ -+ # or any other controller that supports high speed transfers ... -+ define_bool CONFIG_USB_GADGET_DUALSPEED $CONFIG_USB_GADGET_NET2280 -+ -+ if [ "$CONFIG_USB_GADGET_CONTROLLER" = "y" -o "$CONFIG_USB_GADGET_CONTROLLER" = "m" ] ; then -+ -+ # -+ # no reason not to enable more than one gadget driver module, but -+ # for static linking that would make no sense since the usb model -+ # has exactly one of these upstream connections and only one -+ # lowest-level driver can control it. -+ # -+ # gadget drivers are compiled to work on specific hardware, since -+ # -+ # (a) gadget driver need hardware-specific configuration, like what -+ # endpoint names and numbers to use, maxpacket sizes, etc -+ # -+ # (b) specific hardware features like iso endpoints may be required -+ # -+ comment 'USB Gadget Drivers' -+ -+ # FIXME when drivers all use #ifdef CONFIG_USB_GADGET_* tests, -+ # just remove all this driver-specific define_bool logic -+ -+ dep_tristate ' Gadget Zero (DEVELOPMENT)' CONFIG_USB_ZERO $CONFIG_USB_GADGET_CONTROLLER -+ dep_tristate ' Ethernet Gadget (EXPERIMENTAL)' CONFIG_USB_ETH $CONFIG_USB_GADGET_CONTROLLER $CONFIG_NET -+ if [ "$CONFIG_USB_ETH" = "y" -o "$CONFIG_USB_ETH" = "m" ] ; then -+ bool ' RNDIS support (EXPERIMENTAL)' CONFIG_USB_ETH_RNDIS -+ fi -+ dep_tristate ' Gadget Filesystem API (EXPERIMENTAL)' CONFIG_USB_GADGETFS $CONFIG_USB_GADGET_CONTROLLER -+ dep_tristate ' File-backed Storage Gadget (DEVELOPMENT)' CONFIG_USB_FILE_STORAGE $CONFIG_USB_GADGET_CONTROLLER -+ dep_mbool ' File-backed Storage Gadget test mode' CONFIG_USB_FILE_STORAGE_TEST $CONFIG_USB_FILE_STORAGE -+ dep_tristate ' Serial Gadget (EXPERIMENTAL)' CONFIG_USB_G_SERIAL $CONFIG_USB_GADGET_CONTROLLER -+ -+ -+ # enforce the "only one statically linked gadget driver" rule -+ -+ if [ "$CONFIG_USB_ZERO" = "y" ]; then -+ # zero = y -+ define_tristate CONFIG_USB_ETH n -+ define_tristate CONFIG_USB_GADGETFS n -+ define_tristate CONFIG_USB_FILE_STORAGE n -+ define_tristate CONFIG_USB_G_SERIAL n -+ fi -+ -+ if [ "$CONFIG_USB_ETH" = "y" ]; then -+ define_tristate CONFIG_USB_ZERO n -+ # eth = y -+ define_tristate CONFIG_USB_GADGETFS n -+ define_tristate CONFIG_USB_FILE_STORAGE n -+ define_tristate CONFIG_USB_G_SERIAL n -+ fi -+ -+ if [ "$CONFIG_USB_GADGETFS" = "y" ]; then -+ define_tristate CONFIG_USB_ZERO n -+ define_tristate CONFIG_USB_ETH n -+ # gadgetfs = y -+ define_tristate CONFIG_USB_FILE_STORAGE n -+ define_tristate CONFIG_USB_G_SERIAL n -+ fi -+ -+ if [ "$CONFIG_USB_FILE_STORAGE" = "y" ]; then -+ define_tristate CONFIG_USB_ZERO n -+ define_tristate CONFIG_USB_ETH n -+ define_tristate CONFIG_USB_GADGETFS n -+ # file_storage = y -+ define_tristate CONFIG_USB_G_SERIAL n -+ fi -+ -+ if [ "$CONFIG_USB_G_SERIAL" = "y" ]; then -+ define_tristate CONFIG_USB_ZERO n -+ define_tristate CONFIG_USB_ETH n -+ define_tristate CONFIG_USB_GADGETFS n -+ define_tristate CONFIG_USB_FILE_STORAGE n -+ # g_serial = y -+ fi -+ fi -+fi -+endmenu -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/Makefile kernel/drivers/usb/gadget/Makefile ---- /tmp/kernel/drivers/usb/gadget/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/Makefile 2005-04-22 17:53:19.405544237 +0200 -@@ -0,0 +1,58 @@ -+# -+# Makefile for USB peripheral controller and gadget drivers -+# for kbuild 2.4 -+# -+ -+# for static linking -+O_TARGET := built-in.o -+ -+list-multi := g_zero.o g_ether.o gadgetfs.o g_file_storage.o g_serial.o -+ -+obj-$(CONFIG_USB_NET2280) += net2280.o -+obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o -+obj-$(CONFIG_USB_GOKU) += goku_udc.o -+obj-$(CONFIG_USB_SUPERH) += superh_udc.o -+obj-$(CONFIG_USB_N9604) += n9604.o -+ -+# only one of these may be statically linked ... -+controller-$(CONFIG_USB_NET2280) += net2280.o -+controller-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o -+controller-$(CONFIG_USB_GOKU) += goku_udc.o -+controller-$(CONFIG_USB_SUPERH) += superh_udc.o -+controller-$(CONFIG_USB_N9604) += n9604.o -+ -+# ... and only one of these, too; kbuild/kconfig don't help though. -+g_zero-objs := zero.o usbstring.o config.o epautoconf.o -+obj-$(CONFIG_USB_ZERO) += g_zero.o -+ -+g_ether-objs := ether.o usbstring.o config.o epautoconf.o -+obj-$(CONFIG_USB_ETH) += g_ether.o -+ -+ifeq ($(CONFIG_USB_ETH_RNDIS),y) -+ g_ether-objs += rndis.o -+endif -+ -+gadgetfs-objs := inode.o usbstring.o -+obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o -+ -+g_file_storage-objs := file_storage.o usbstring.o config.o \ -+ epautoconf.o -+obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o -+ -+g_serial-objs := gserial.o usbstring.o epautoconf.o -+obj-$(CONFIG_USB_G_SERIAL) += g_serial.o -+ -+export-objs := $(controller-y) $(controller-m) -+ -+include $(TOPDIR)/Rules.make -+ -+g_zero.o: $(g_zero-objs) -+ $(LD) -r -o $@ $(g_zero-objs) -+g_ether.o: $(g_ether-objs) -+ $(LD) -r -o $@ $(g_ether-objs) -+gadgetfs.o: $(gadgetfs-objs) -+ $(LD) -r -o $@ $(gadgetfs-objs) -+g_file_storage.o: $(g_file_storage-objs) -+ $(LD) -r -o $@ $(g_file_storage-objs) -+g_serial.o: $(g_serial-objs) -+ $(LD) -r -o $@ $(g_serial-objs) -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/config.c kernel/drivers/usb/gadget/config.c ---- /tmp/kernel/drivers/usb/gadget/config.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/config.c 2005-04-22 17:53:19.408543749 +0200 -@@ -0,0 +1,116 @@ -+/* -+ * usb/gadget/config.c -- simplify building config descriptors -+ * -+ * Copyright (C) 2003 David Brownell -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/list.h> -+#include <linux/string.h> -+#include <asm/byteorder.h> -+ -+#include <linux/usb_ch9.h> -+ -+ -+/** -+ * usb_descriptor_fillbuf - fill buffer with descriptors -+ * @buf: Buffer to be filled -+ * @buflen: Size of buf -+ * @src: Array of descriptor pointers, terminated by null pointer. -+ * -+ * Copies descriptors into the buffer, returning the length or a -+ * negative error code if they can't all be copied. Useful when -+ * assembling descriptors for an associated set of interfaces used -+ * as part of configuring a composite device; or in other cases where -+ * sets of descriptors need to be marshaled. -+ */ -+int -+usb_descriptor_fillbuf(void *buf, unsigned buflen, -+ const struct usb_descriptor_header **src) -+{ -+ u8 *dest = buf; -+ -+ if (!src) -+ return -EINVAL; -+ -+ /* fill buffer from src[] until null descriptor ptr */ -+ for (; 0 != *src; src++) { -+ unsigned len = (*src)->bLength; -+ -+ if (len > buflen) -+ return -EINVAL; -+ memcpy(dest, *src, len); -+ buflen -= len; -+ dest += len; -+ } -+ return dest - (u8 *)buf; -+} -+ -+ -+/** -+ * usb_gadget_config_buf - builts a complete configuration descriptor -+ * @config: Header for the descriptor, including characteristics such -+ * as power requirements and number of interfaces. -+ * @desc: Null-terminated vector of pointers to the descriptors (interface, -+ * endpoint, etc) defining all functions in this device configuration. -+ * @buf: Buffer for the resulting configuration descriptor. -+ * @length: Length of buffer. If this is not big enough to hold the -+ * entire configuration descriptor, an error code will be returned. -+ * -+ * This copies descriptors into the response buffer, building a descriptor -+ * for that configuration. It returns the buffer length or a negative -+ * status code. The config.wTotalLength field is set to match the length -+ * of the result, but other descriptor fields (including power usage and -+ * interface count) must be set by the caller. -+ * -+ * Gadget drivers could use this when constructing a config descriptor -+ * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the -+ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed. -+ */ -+int usb_gadget_config_buf( -+ const struct usb_config_descriptor *config, -+ void *buf, -+ unsigned length, -+ const struct usb_descriptor_header **desc -+) -+{ -+ struct usb_config_descriptor *cp = buf; -+ int len; -+ -+ /* config descriptor first */ -+ if (length < USB_DT_CONFIG_SIZE || !desc) -+ return -EINVAL; -+ *cp = *config; -+ -+ /* then interface/endpoint/class/vendor/... */ -+ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, -+ length - USB_DT_CONFIG_SIZE, desc); -+ if (len < 0) -+ return len; -+ len += USB_DT_CONFIG_SIZE; -+ if (len > 0xffff) -+ return -EINVAL; -+ -+ /* patch up the config descriptor */ -+ cp->bLength = USB_DT_CONFIG_SIZE; -+ cp->bDescriptorType = USB_DT_CONFIG; -+ cp->wTotalLength = cpu_to_le16(len); -+ cp->bmAttributes |= USB_CONFIG_ATT_ONE; -+ return len; -+} -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/epautoconf.c kernel/drivers/usb/gadget/epautoconf.c ---- /tmp/kernel/drivers/usb/gadget/epautoconf.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/epautoconf.c 2005-04-22 17:53:19.410543423 +0200 -@@ -0,0 +1,311 @@ -+/* -+ * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers -+ * -+ * Copyright (C) 2004 David Brownell -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/init.h> -+#include <linux/types.h> -+#include <linux/list.h> -+#include <linux/errno.h> -+#include <linux/ctype.h> -+#include <linux/string.h> -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include <asm/byteorder.h> -+ -+#include "gadget_chips.h" -+ -+ -+/* we must assign addresses for configurable endpoints (like net2280) */ -+static __initdata unsigned epnum; -+ -+// #define MANY_ENDPOINTS -+#ifdef MANY_ENDPOINTS -+/* more than 15 configurable endpoints */ -+static __initdata unsigned in_epnum; -+#endif -+ -+ -+/* -+ * This should work with endpoints from controller drivers sharing the -+ * same endpoint naming convention. By example: -+ * -+ * - ep1, ep2, ... address is fixed, not direction or type -+ * - ep1in, ep2out, ... address and direction are fixed, not type -+ * - ep1-bulk, ep2-bulk, ... address and type are fixed, not direction -+ * - ep1in-bulk, ep2out-iso, ... all three are fixed -+ * - ep-* ... no functionality restrictions -+ * -+ * Type suffixes are "-bulk", "-iso", or "-int". Numbers are decimal. -+ * Less common restrictions are implied by gadget_is_*(). -+ * -+ * NOTE: each endpoint is unidirectional, as specified by its USB -+ * descriptor; and isn't specific to a configuration or altsetting. -+ */ -+static int __init -+ep_matches ( -+ struct usb_gadget *gadget, -+ struct usb_ep *ep, -+ struct usb_endpoint_descriptor *desc -+) -+{ -+ u8 type; -+ const char *tmp; -+ u16 max; -+ -+ /* endpoint already claimed? */ -+ if (0 != ep->driver_data) -+ return 0; -+ -+ /* only support ep0 for portable CONTROL traffic */ -+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; -+ if (USB_ENDPOINT_XFER_CONTROL == type) -+ return 0; -+ -+ /* some other naming convention */ -+ if ('e' != ep->name[0]) -+ return 0; -+ -+ /* type-restriction: "-iso", "-bulk", or "-int". -+ * direction-restriction: "in", "out". -+ */ -+ if ('-' != ep->name[2]) { -+ tmp = strrchr (ep->name, '-'); -+ if (tmp) { -+ switch (type) { -+ case USB_ENDPOINT_XFER_INT: -+ /* bulk endpoints handle interrupt transfers, -+ * except the toggle-quirky iso-synch kind -+ */ -+ if ('s' == tmp[2]) // == "-iso" -+ return 0; -+ /* for now, avoid PXA "interrupt-in"; -+ * it's documented as never using DATA1. -+ */ -+ if (gadget_is_pxa (gadget) -+ && 'i' == tmp [1]) -+ return 0; -+ break; -+ case USB_ENDPOINT_XFER_BULK: -+ if ('b' != tmp[1]) // != "-bulk" -+ return 0; -+ break; -+ case USB_ENDPOINT_XFER_ISOC: -+ if ('s' != tmp[2]) // != "-iso" -+ return 0; -+ } -+ } else { -+ tmp = ep->name + strlen (ep->name); -+ } -+ -+ /* direction-restriction: "..in-..", "out-.." */ -+ tmp--; -+ if (!isdigit (*tmp)) { -+ if (desc->bEndpointAddress & USB_DIR_IN) { -+ if ('n' != *tmp) -+ return 0; -+ } else { -+ if ('t' != *tmp) -+ return 0; -+ } -+ } -+ } -+ -+ /* endpoint maxpacket size is an input parameter, except for bulk -+ * where it's an output parameter representing the full speed limit. -+ * the usb spec fixes high speed bulk maxpacket at 512 bytes. -+ */ -+ max = 0x7ff & le16_to_cpup (&desc->wMaxPacketSize); -+ switch (type) { -+ case USB_ENDPOINT_XFER_INT: -+ /* INT: limit 64 bytes full speed, 1024 high speed */ -+ if (!gadget->is_dualspeed && max > 64) -+ return 0; -+ /* FALLTHROUGH */ -+ -+ case USB_ENDPOINT_XFER_ISOC: -+ /* ISO: limit 1023 bytes full speed, 1024 high speed */ -+ if (ep->maxpacket < max) -+ return 0; -+ if (!gadget->is_dualspeed && max > 1023) -+ return 0; -+ -+ /* BOTH: "high bandwidth" works only at high speed */ -+ if ((desc->wMaxPacketSize & __constant_cpu_to_le16(3<<11))) { -+ if (!gadget->is_dualspeed) -+ return 0; -+ /* configure your hardware with enough buffering!! */ -+ } -+ break; -+ } -+ -+ /* MATCH!! */ -+ -+ /* report address */ -+ if (isdigit (ep->name [2])) { -+ u8 num = simple_strtol (&ep->name [2], NULL, 10); -+ desc->bEndpointAddress |= num; -+#ifdef MANY_ENDPOINTS -+ } else if (desc->bEndpointAddress & USB_DIR_IN) { -+ if (++in_epnum > 15) -+ return 0; -+ desc->bEndpointAddress = USB_DIR_IN | in_epnum; -+#endif -+ } else { -+ if (++epnum > 15) -+ return 0; -+ desc->bEndpointAddress |= epnum; -+ } -+ -+ /* report (variable) full speed bulk maxpacket */ -+ if (USB_ENDPOINT_XFER_BULK == type) { -+ int size = ep->maxpacket; -+ -+ /* min() doesn't work on bitfields with gcc-3.5 */ -+ if (size > 64) -+ size = 64; -+ desc->wMaxPacketSize = cpu_to_le16(size); -+ } -+ return 1; -+} -+ -+static struct usb_ep * __init -+find_ep (struct usb_gadget *gadget, const char *name) -+{ -+ struct usb_ep *ep; -+ -+ list_for_each_entry (ep, &gadget->ep_list, ep_list) { -+ if (0 == strcmp (ep->name, name)) -+ return ep; -+ } -+ return NULL; -+} -+ -+/** -+ * usb_ep_autoconfig - choose an endpoint matching the descriptor -+ * @gadget: The device to which the endpoint must belong. -+ * @desc: Endpoint descriptor, with endpoint direction and transfer mode -+ * initialized. For periodic transfers, the maximum packet -+ * size must also be initialized. This is modified on success. -+ * -+ * By choosing an endpoint to use with the specified descriptor, this -+ * routine simplifies writing gadget drivers that work with multiple -+ * USB device controllers. The endpoint would be passed later to -+ * usb_ep_enable(), along with some descriptor. -+ * -+ * That second descriptor won't always be the same as the first one. -+ * For example, isochronous endpoints can be autoconfigured for high -+ * bandwidth, and then used in several lower bandwidth altsettings. -+ * Also, high and full speed descriptors will be different. -+ * -+ * Be sure to examine and test the results of autoconfiguration on your -+ * hardware. This code may not make the best choices about how to use the -+ * USB controller, and it can't know all the restrictions that may apply. -+ * Some combinations of driver and hardware won't be able to autoconfigure. -+ * -+ * On success, this returns an un-claimed usb_ep, and modifies the endpoint -+ * descriptor bEndpointAddress. For bulk endpoints, the wMaxPacket value -+ * is initialized as if the endpoint were used at full speed. To prevent -+ * the endpoint from being returned by a later autoconfig call, claim it -+ * by assigning ep->driver_data to some non-null value. -+ * -+ * On failure, this returns a null endpoint descriptor. -+ */ -+struct usb_ep * __init usb_ep_autoconfig ( -+ struct usb_gadget *gadget, -+ struct usb_endpoint_descriptor *desc -+) -+{ -+ struct usb_ep *ep; -+ u8 type; -+ -+ type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; -+ -+ /* First, apply chip-specific "best usage" knowledge. -+ * This might make a good usb_gadget_ops hook ... -+ */ -+ if (gadget_is_net2280 (gadget) && type == USB_ENDPOINT_XFER_INT) { -+ /* ep-e, ep-f are PIO with only 64 byte fifos */ -+ ep = find_ep (gadget, "ep-e"); -+ if (ep && ep_matches (gadget, ep, desc)) -+ return ep; -+ ep = find_ep (gadget, "ep-f"); -+ if (ep && ep_matches (gadget, ep, desc)) -+ return ep; -+ -+ } else if (gadget_is_goku (gadget)) { -+ if (USB_ENDPOINT_XFER_INT == type) { -+ /* single buffering is enough */ -+ ep = find_ep (gadget, "ep3-bulk"); -+ if (ep && ep_matches (gadget, ep, desc)) -+ return ep; -+ } else if (USB_ENDPOINT_XFER_BULK == type -+ && (USB_DIR_IN & desc->bEndpointAddress)) { -+ /* DMA may be available */ -+ ep = find_ep (gadget, "ep2-bulk"); -+ if (ep && ep_matches (gadget, ep, desc)) -+ return ep; -+ } -+ -+ } else if (gadget_is_sh (gadget) && USB_ENDPOINT_XFER_INT == type) { -+ /* single buffering is enough; maybe 8 byte fifo is too */ -+ ep = find_ep (gadget, "ep3in-bulk"); -+ if (ep && ep_matches (gadget, ep, desc)) -+ return ep; -+ -+ } else if (gadget_is_mq11xx (gadget) && USB_ENDPOINT_XFER_INT == type) { -+ ep = find_ep (gadget, "ep1-bulk"); -+ if (ep && ep_matches (gadget, ep, desc)) -+ return ep; -+ } -+ -+ /* Second, look at endpoints until an unclaimed one looks usable */ -+ list_for_each_entry (ep, &gadget->ep_list, ep_list) { -+ if (ep_matches (gadget, ep, desc)) -+ return ep; -+ } -+ -+ /* Fail */ -+ return NULL; -+} -+ -+/** -+ * usb_ep_autoconfig_reset - reset endpoint autoconfig state -+ * @gadget: device for which autoconfig state will be reset -+ * -+ * Use this for devices where one configuration may need to assign -+ * endpoint resources very differently from the next one. It clears -+ * state such as ep->driver_data and the record of assigned endpoints -+ * used by usb_ep_autoconfig(). -+ */ -+void __init usb_ep_autoconfig_reset (struct usb_gadget *gadget) -+{ -+ struct usb_ep *ep; -+ -+ list_for_each_entry (ep, &gadget->ep_list, ep_list) { -+ ep->driver_data = NULL; -+ } -+#ifdef MANY_ENDPOINTS -+ in_epnum = 0; -+#endif -+ epnum = 0; -+} -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/ether.c kernel/drivers/usb/gadget/ether.c ---- /tmp/kernel/drivers/usb/gadget/ether.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/ether.c 2005-04-22 18:01:31.044861540 +0200 -@@ -0,0 +1,2734 @@ -+/* -+ * ether.c -- Ethernet gadget driver, with CDC and non-CDC options -+ * -+ * Copyright (C) 2003-2005 David Brownell -+ * Copyright (C) 2003-2004 Robert Schwebel, Benedikt Spranger -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+ -+// #define DEBUG 1 -+// #define VERBOSE -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/delay.h> -+#include <linux/ioport.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/smp_lock.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+#include <linux/uts.h> -+#include <linux/version.h> -+#include <linux/moduleparam.h> -+#include <linux/ctype.h> -+ -+#include <asm/byteorder.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/uaccess.h> -+#include <asm/unaligned.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_cdc.h> -+#include <linux/usb_gadget.h> -+ -+#include <linux/random.h> -+#include <linux/netdevice.h> -+#include <linux/etherdevice.h> -+#include <linux/ethtool.h> -+ -+#include "gadget_chips.h" -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Ethernet gadget driver -- with CDC and non-CDC options -+ * Builds on hardware support for a full duplex link. -+ * -+ * CDC Ethernet is the standard USB solution for sending Ethernet frames -+ * using USB. Real hardware tends to use the same framing protocol but look -+ * different for control features. This driver strongly prefers to use -+ * this USB-IF standard as its open-systems interoperability solution; -+ * most host side USB stacks (except from Microsoft) support it. -+ * -+ * There's some hardware that can't talk CDC. We make that hardware -+ * implement a "minimalist" vendor-agnostic CDC core: same framing, but -+ * link-level setup only requires activating the configuration. -+ * Linux supports it, but other host operating systems may not. -+ * (This is a subset of CDC Ethernet.) -+ * -+ * A third option is also in use. Rather than CDC Ethernet, or something -+ * simpler, Microsoft pushes their own approach: RNDIS. The published -+ * RNDIS specs are ambiguous and appear to be incomplete, and are also -+ * needlessly complex. -+ */ -+ -+#define DRIVER_DESC "Ethernet Gadget" -+#define DRIVER_VERSION "Equinox 2004" -+ -+static const char shortname [] = "ether"; -+static const char driver_desc [] = DRIVER_DESC; -+ -+#define RX_EXTRA 20 /* guard against rx overflows */ -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+#include "rndis.h" -+#else -+#define rndis_init() 0 -+#define rndis_exit() do{}while(0) -+#endif -+ -+/* 2.6-compat */ -+#ifndef container_of -+#define container_of list_entry -+#endif -+ -+/** PO: really needed? */ -+#include <linux/tqueue.h> -+#define work_struct tq_struct -+#define INIT_WORK INIT_TQUEUE -+#define schedule_work schedule_task -+#define flush_scheduled_work flush_scheduled_tasks -+ -+static void random_ether_addr (u8 *addr) -+{ -+ get_random_bytes (addr, ETH_ALEN); -+ addr [0] &= 0xfe; // clear multicast bit -+ addr [0] |= 0x02; // set local assignment bit (IEEE802) -+} -+ -+/* CDC and RNDIS support the same host-chosen outgoing packet filters. */ -+#define DEFAULT_FILTER (USB_CDC_PACKET_TYPE_BROADCAST \ -+ |USB_CDC_PACKET_TYPE_DIRECTED) -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+struct eth_dev { -+ spinlock_t lock; -+ struct usb_gadget *gadget; -+ struct usb_request *req; /* for control responses */ -+ struct usb_request *stat_req; /* for cdc & rndis status */ -+ -+ u8 config; -+ struct usb_ep *in_ep, *out_ep, *status_ep; -+ const struct usb_endpoint_descriptor -+ *in, *out, *status; -+ struct list_head tx_reqs, rx_reqs; -+ -+ struct net_device *net; -+ struct net_device_stats stats; -+ atomic_t tx_qlen; -+ -+ struct work_struct work; -+ unsigned zlp:1; -+ unsigned cdc:1; -+ unsigned rndis:1; -+ unsigned suspended:1; -+ u16 cdc_filter; -+ unsigned long todo; -+#define WORK_RX_MEMORY 0 -+ int rndis_config; -+ u8 host_mac [ETH_ALEN]; -+}; -+ -+/* This version autoconfigures as much as possible at run-time. -+ * -+ * It also ASSUMES a self-powered device, without remote wakeup, -+ * although remote wakeup support would make sense. -+ */ -+static const char *EP_IN_NAME; -+static const char *EP_OUT_NAME; -+static const char *EP_STATUS_NAME; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! -+ * Instead: allocate your own, using normal USB-IF procedures. -+ */ -+ -+/* Thanks to NetChip Technologies for donating this product ID. -+ * It's for devices with only CDC Ethernet configurations. -+ */ -+#define CDC_VENDOR_NUM 0x0525 /* NetChip */ -+#define CDC_PRODUCT_NUM 0xa4a1 /* Linux-USB Ethernet Gadget */ -+ -+/* For hardware that can't talk CDC, we use the same vendor ID that -+ * ARM Linux has used for ethernet-over-usb, both with sa1100 and -+ * with pxa250. We're protocol-compatible, if the host-side drivers -+ * use the endpoint descriptors. bcdDevice (version) is nonzero, so -+ * drivers that need to hard-wire endpoint numbers have a hook. -+ * -+ * The protocol is a minimal subset of CDC Ether, which works on any bulk -+ * hardware that's not deeply broken ... even on hardware that can't talk -+ * RNDIS (like SA-1100, with no interrupt endpoint, or anything that -+ * doesn't handle control-OUT). -+ */ -+#define SIMPLE_VENDOR_NUM 0x049f -+#define SIMPLE_PRODUCT_NUM 0x505a -+ -+/* For hardware that can talk RNDIS and either of the above protocols, -+ * use this ID ... the windows INF files will know it. Unless it's -+ * used with CDC Ethernet, Linux 2.4 hosts will need updates to choose -+ * the non-RNDIS configuration. -+ */ -+#define RNDIS_VENDOR_NUM 0x0525 /* NetChip */ -+#define RNDIS_PRODUCT_NUM 0xa4a2 /* Ethernet/RNDIS Gadget */ -+ -+ -+/* Some systems will want different product identifers published in the -+ * device descriptor, either numbers or strings or both. These string -+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters). -+ */ -+ -+static ushort __initdata idVendor; -+MODULE_PARM(idVendor, "h"); -+MODULE_PARM_DESC(idVendor, "USB Vendor ID"); -+ -+static ushort __initdata idProduct; -+MODULE_PARM(idProduct, "h"); -+MODULE_PARM_DESC(idProduct, "USB Product ID"); -+ -+static ushort __initdata bcdDevice; -+MODULE_PARM(bcdDevice, "h"); -+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)"); -+ -+static char *__initdata iManufacturer; -+MODULE_PARM(iManufacturer, "s"); -+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string"); -+ -+static char *__initdata iProduct; -+MODULE_PARM(iProduct, "s"); -+MODULE_PARM_DESC(iProduct, "USB Product string"); -+ -+/* initial value, changed by "ifconfig usb0 hw ether xx:xx:xx:xx:xx:xx" */ -+static char *__initdata dev_addr; -+MODULE_PARM(dev_addr, "s"); -+MODULE_PARM_DESC(dev_addr, "Device Ethernet Address"); -+ -+/* this address is invisible to ifconfig */ -+static char *__initdata host_addr; -+MODULE_PARM(host_addr, "s"); -+MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Include CDC support if we could run on CDC-capable hardware. */ -+ -+#ifdef CONFIG_USB_GADGET_NET2280 -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_DUMMY_HCD -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_GOKU -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_LH7A40X -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_MQ11XX -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_OMAP -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_N9604 -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_PXA27X -+#define DEV_CONFIG_CDC -+#endif -+ -+#ifdef CONFIG_USB_GADGET_AT91 -+#define DEV_CONFIG_CDC -+#endif -+ -+ -+/* For CDC-incapable hardware, choose the simple cdc subset. -+ * Anything that talks bulk (without notable bugs) can do this. -+ */ -+#ifdef CONFIG_USB_GADGET_PXA2XX -+#define DEV_CONFIG_SUBSET -+#endif -+ -+#ifdef CONFIG_USB_GADGET_SH -+#define DEV_CONFIG_SUBSET -+#endif -+ -+#ifdef CONFIG_USB_GADGET_SA1100 -+/* use non-CDC for backwards compatibility */ -+#define DEV_CONFIG_SUBSET -+#endif -+ -+#ifdef CONFIG_USB_GADGET_S3C2410 -+#define DEV_CONFIG_CDC -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* "main" config is either CDC, or its simple subset */ -+static inline int is_cdc(struct eth_dev *dev) -+{ -+#if !defined(DEV_CONFIG_SUBSET) -+ return 1; /* only cdc possible */ -+#elif !defined (DEV_CONFIG_CDC) -+ return 0; /* only subset possible */ -+#else -+ return dev->cdc; /* depends on what hardware we found */ -+#endif -+} -+ -+/* "secondary" RNDIS config may sometimes be activated */ -+static inline int rndis_active(struct eth_dev *dev) -+{ -+#ifdef CONFIG_USB_ETH_RNDIS -+ return dev->rndis; -+#else -+ return 0; -+#endif -+} -+ -+#define subset_active(dev) (!is_cdc(dev) && !rndis_active(dev)) -+#define cdc_active(dev) ( is_cdc(dev) && !rndis_active(dev)) -+ -+ -+ -+#define DEFAULT_QLEN 2 /* double buffering by default */ -+ -+/* peak bulk transfer bits-per-second */ -+#define HS_BPS (13 * 512 * 8 * 1000 * 8) -+#define FS_BPS (19 * 64 * 1 * 1000 * 8) -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ -+static unsigned qmult = 5; -+MODULE_PARM(qmult, "i"); -+ -+ -+/* for dual-speed hardware, use deeper queues at highspeed */ -+#define qlen(gadget) \ -+ (DEFAULT_QLEN*((gadget->speed == USB_SPEED_HIGH) ? qmult : 1)) -+ -+/* also defer IRQs on highspeed TX */ -+#define TX_DELAY qmult -+ -+#define BITRATE(g) (((g)->speed == USB_SPEED_HIGH) ? HS_BPS : FS_BPS) -+ -+#else /* full speed (low speed doesn't do bulk) */ -+#define qlen(gadget) DEFAULT_QLEN -+ -+#define BITRATE(g) FS_BPS -+#endif -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define xprintk(d,level,fmt,args...) \ -+ printk(level "%s: " fmt , (d)->net->name , ## args) -+ -+#ifdef DEBUG -+#undef DEBUG -+#define DEBUG(dev,fmt,args...) \ -+ xprintk(dev , KERN_DEBUG , fmt , ## args) -+#else -+#define DEBUG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+#ifdef VERBOSE -+#define VDEBUG DEBUG -+#else -+#define VDEBUG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+#define ERROR(dev,fmt,args...) \ -+ xprintk(dev , KERN_ERR , fmt , ## args) -+#define WARN(dev,fmt,args...) \ -+ xprintk(dev , KERN_WARNING , fmt , ## args) -+#define INFO(dev,fmt,args...) \ -+ xprintk(dev , KERN_INFO , fmt , ## args) -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB DRIVER HOOKUP (to the hardware driver, below us), mostly -+ * ep0 implementation: descriptors, config management, setup(). -+ * also optional class-specific notification interrupt transfer. -+ */ -+ -+/* -+ * DESCRIPTORS ... most are static, but strings and (full) configuration -+ * descriptors are built on demand. For now we do either full CDC, or -+ * our simple subset, with RNDIS as an optional second configuration. -+ * -+ * RNDIS includes some CDC ACM descriptors ... like CDC Ethernet. But -+ * the class descriptors match a modem (they're ignored; it's really just -+ * Ethernet functionality), they don't need the NOP altsetting, and the -+ * status transfer endpoint isn't optional. -+ */ -+ -+#define STRING_MANUFACTURER 1 -+#define STRING_PRODUCT 2 -+#define STRING_ETHADDR 3 -+#define STRING_DATA 4 -+#define STRING_CONTROL 5 -+#define STRING_RNDIS_CONTROL 6 -+#define STRING_CDC 7 -+#define STRING_SUBSET 8 -+#define STRING_RNDIS 9 -+ -+#define USB_BUFSIZ 256 /* holds our biggest descriptor */ -+ -+/* -+ * This device advertises one configuration, eth_config, unless RNDIS -+ * is enabled (rndis_config) on hardware supporting at least two configs. -+ * -+ * NOTE: Controllers like superh_udc should probably be able to use -+ * an RNDIS-only configuration. -+ * -+ * FIXME define some higher-powered configurations to make it easier -+ * to recharge batteries ... -+ */ -+ -+#define DEV_CONFIG_VALUE 1 /* cdc or subset */ -+#define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ -+ -+static struct usb_device_descriptor -+device_desc = { -+ .bLength = sizeof device_desc, -+ .bDescriptorType = USB_DT_DEVICE, -+ -+ .bcdUSB = __constant_cpu_to_le16 (0x0200), -+ -+ .bDeviceClass = USB_CLASS_COMM, -+ .bDeviceSubClass = 0, -+ .bDeviceProtocol = 0, -+ -+ .idVendor = __constant_cpu_to_le16 (CDC_VENDOR_NUM), -+ .idProduct = __constant_cpu_to_le16 (CDC_PRODUCT_NUM), -+ .iManufacturer = STRING_MANUFACTURER, -+ .iProduct = STRING_PRODUCT, -+ .bNumConfigurations = 1, -+}; -+ -+static struct usb_otg_descriptor -+otg_descriptor = { -+ .bLength = sizeof otg_descriptor, -+ .bDescriptorType = USB_DT_OTG, -+ -+ .bmAttributes = USB_OTG_SRP, -+}; -+ -+static struct usb_config_descriptor -+eth_config = { -+ .bLength = sizeof eth_config, -+ .bDescriptorType = USB_DT_CONFIG, -+ -+ /* compute wTotalLength on the fly */ -+ .bNumInterfaces = 2, -+ .bConfigurationValue = DEV_CONFIG_VALUE, -+ .iConfiguration = STRING_CDC, -+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -+ .bMaxPower = 50, -+}; -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+static struct usb_config_descriptor -+rndis_config = { -+ .bLength = sizeof rndis_config, -+ .bDescriptorType = USB_DT_CONFIG, -+ -+ /* compute wTotalLength on the fly */ -+ .bNumInterfaces = 2, -+ .bConfigurationValue = DEV_RNDIS_CONFIG_VALUE, -+ .iConfiguration = STRING_RNDIS, -+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -+ .bMaxPower = 50, -+}; -+#endif -+ -+/* -+ * Compared to the simple CDC subset, the full CDC Ethernet model adds -+ * three class descriptors, two interface descriptors, optional status -+ * endpoint. Both have a "data" interface and two bulk endpoints. -+ * There are also differences in how control requests are handled. -+ * -+ * RNDIS shares a lot with CDC-Ethernet, since it's a variant of -+ * the CDC-ACM (modem) spec. -+ */ -+ -+#ifdef DEV_CONFIG_CDC -+static struct usb_interface_descriptor -+control_intf = { -+ .bLength = sizeof control_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bInterfaceNumber = 0, -+ /* status endpoint is optional; this may be patched later */ -+ .bNumEndpoints = 1, -+ .bInterfaceClass = USB_CLASS_COMM, -+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET, -+ .bInterfaceProtocol = USB_CDC_PROTO_NONE, -+ .iInterface = STRING_CONTROL, -+}; -+#endif -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+static const struct usb_interface_descriptor -+rndis_control_intf = { -+ .bLength = sizeof rndis_control_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bInterfaceNumber = 0, -+ .bNumEndpoints = 1, -+ .bInterfaceClass = USB_CLASS_COMM, -+ .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, -+ .bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR, -+ .iInterface = STRING_RNDIS_CONTROL, -+}; -+#endif -+ -+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -+ -+static const struct usb_cdc_header_desc header_desc = { -+ .bLength = sizeof header_desc, -+ .bDescriptorType = USB_DT_CS_INTERFACE, -+ .bDescriptorSubType = USB_CDC_HEADER_TYPE, -+ -+ .bcdCDC = __constant_cpu_to_le16 (0x0110), -+}; -+ -+static const struct usb_cdc_union_desc union_desc = { -+ .bLength = sizeof union_desc, -+ .bDescriptorType = USB_DT_CS_INTERFACE, -+ .bDescriptorSubType = USB_CDC_UNION_TYPE, -+ -+ .bMasterInterface0 = 0, /* index of control interface */ -+ .bSlaveInterface0 = 1, /* index of DATA interface */ -+}; -+ -+#endif /* CDC || RNDIS */ -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ -+static const struct usb_cdc_call_mgmt_descriptor call_mgmt_descriptor = { -+ .bLength = sizeof call_mgmt_descriptor, -+ .bDescriptorType = USB_DT_CS_INTERFACE, -+ .bDescriptorSubType = USB_CDC_CALL_MANAGEMENT_TYPE, -+ -+ .bmCapabilities = 0x00, -+ .bDataInterface = 0x01, -+}; -+ -+static struct usb_cdc_acm_descriptor acm_descriptor = { -+ .bLength = sizeof acm_descriptor, -+ .bDescriptorType = USB_DT_CS_INTERFACE, -+ .bDescriptorSubType = USB_CDC_ACM_TYPE, -+ -+ .bmCapabilities = 0x00, -+}; -+ -+#endif -+ -+#ifdef DEV_CONFIG_CDC -+ -+static const struct usb_cdc_ether_desc ether_desc = { -+ .bLength = sizeof ether_desc, -+ .bDescriptorType = USB_DT_CS_INTERFACE, -+ .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, -+ -+ /* this descriptor actually adds value, surprise! */ -+ .iMACAddress = STRING_ETHADDR, -+ .bmEthernetStatistics = __constant_cpu_to_le32 (0), /* no statistics */ -+ .wMaxSegmentSize = __constant_cpu_to_le16 (ETH_FRAME_LEN), -+ .wNumberMCFilters = __constant_cpu_to_le16 (0), -+ .bNumberPowerFilters = 0, -+}; -+ -+#endif -+ -+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -+ -+/* include the status endpoint if we can, even where it's optional. -+ * use wMaxPacketSize big enough to fit CDC_NOTIFY_SPEED_CHANGE in one -+ * packet, to simplify cancelation; and a big transfer interval, to -+ * waste less bandwidth. -+ * -+ * some drivers (like Linux 2.4 cdc-ether!) "need" it to exist even -+ * if they ignore the connect/disconnect notifications that real aether -+ * can provide. more advanced cdc configurations might want to support -+ * encapsulated commands (vendor-specific, using control-OUT). -+ * -+ * RNDIS requires the status endpoint, since it uses that encapsulation -+ * mechanism for its funky RPC scheme. -+ */ -+ -+#define LOG2_STATUS_INTERVAL_MSEC 5 /* 1 << 5 == 32 msec */ -+#define STATUS_BYTECOUNT 16 /* 8 byte header + data */ -+ -+static struct usb_endpoint_descriptor -+fs_status_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), -+ .bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC, -+}; -+#endif -+ -+#ifdef DEV_CONFIG_CDC -+ -+/* the default data interface has no endpoints ... */ -+ -+static const struct usb_interface_descriptor -+data_nop_intf = { -+ .bLength = sizeof data_nop_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bInterfaceNumber = 1, -+ .bAlternateSetting = 0, -+ .bNumEndpoints = 0, -+ .bInterfaceClass = USB_CLASS_CDC_DATA, -+ .bInterfaceSubClass = 0, -+ .bInterfaceProtocol = 0, -+}; -+ -+/* ... but the "real" data interface has two bulk endpoints */ -+ -+static const struct usb_interface_descriptor -+data_intf = { -+ .bLength = sizeof data_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bInterfaceNumber = 1, -+ .bAlternateSetting = 1, -+ .bNumEndpoints = 2, -+ .bInterfaceClass = USB_CLASS_CDC_DATA, -+ .bInterfaceSubClass = 0, -+ .bInterfaceProtocol = 0, -+ .iInterface = STRING_DATA, -+}; -+ -+#endif -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ -+/* RNDIS doesn't activate by changing to the "real" altsetting */ -+ -+static const struct usb_interface_descriptor -+rndis_data_intf = { -+ .bLength = sizeof rndis_data_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bInterfaceNumber = 1, -+ .bAlternateSetting = 0, -+ .bNumEndpoints = 2, -+ .bInterfaceClass = USB_CLASS_CDC_DATA, -+ .bInterfaceSubClass = 0, -+ .bInterfaceProtocol = 0, -+ .iInterface = STRING_DATA, -+}; -+ -+#endif -+ -+#ifdef DEV_CONFIG_SUBSET -+ -+/* -+ * "Simple" CDC-subset option is a simple vendor-neutral model that most -+ * full speed controllers can handle: one interface, two bulk endpoints. -+ */ -+ -+static const struct usb_interface_descriptor -+subset_data_intf = { -+ .bLength = sizeof subset_data_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bInterfaceNumber = 0, -+ .bAlternateSetting = 0, -+ .bNumEndpoints = 2, -+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, -+ .bInterfaceSubClass = 0, -+ .bInterfaceProtocol = 0, -+ .iInterface = STRING_DATA, -+}; -+ -+#endif /* SUBSET */ -+ -+ -+static struct usb_endpoint_descriptor -+fs_source_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+}; -+ -+static struct usb_endpoint_descriptor -+fs_sink_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_OUT, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+}; -+ -+static const struct usb_descriptor_header *fs_eth_function [11] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+#ifdef DEV_CONFIG_CDC -+ /* "cdc" mode descriptors */ -+ (struct usb_descriptor_header *) &control_intf, -+ (struct usb_descriptor_header *) &header_desc, -+ (struct usb_descriptor_header *) &union_desc, -+ (struct usb_descriptor_header *) ðer_desc, -+ /* NOTE: status endpoint may need to be removed */ -+ (struct usb_descriptor_header *) &fs_status_desc, -+ /* data interface, with altsetting */ -+ (struct usb_descriptor_header *) &data_nop_intf, -+ (struct usb_descriptor_header *) &data_intf, -+ (struct usb_descriptor_header *) &fs_source_desc, -+ (struct usb_descriptor_header *) &fs_sink_desc, -+ NULL, -+#endif /* DEV_CONFIG_CDC */ -+}; -+ -+static inline void __init fs_subset_descriptors(void) -+{ -+#ifdef DEV_CONFIG_SUBSET -+ fs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; -+ fs_eth_function[2] = (struct usb_descriptor_header *) &fs_source_desc; -+ fs_eth_function[3] = (struct usb_descriptor_header *) &fs_sink_desc; -+ fs_eth_function[4] = NULL; -+#else -+ fs_eth_function[1] = NULL; -+#endif -+} -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+static const struct usb_descriptor_header *fs_rndis_function [] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+ /* control interface matches ACM, not Ethernet */ -+ (struct usb_descriptor_header *) &rndis_control_intf, -+ (struct usb_descriptor_header *) &header_desc, -+ (struct usb_descriptor_header *) &call_mgmt_descriptor, -+ (struct usb_descriptor_header *) &acm_descriptor, -+ (struct usb_descriptor_header *) &union_desc, -+ (struct usb_descriptor_header *) &fs_status_desc, -+ /* data interface has no altsetting */ -+ (struct usb_descriptor_header *) &rndis_data_intf, -+ (struct usb_descriptor_header *) &fs_source_desc, -+ (struct usb_descriptor_header *) &fs_sink_desc, -+ NULL, -+}; -+#endif -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ -+/* -+ * usb 2.0 devices need to expose both high speed and full speed -+ * descriptors, unless they only run at full speed. -+ */ -+ -+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -+static struct usb_endpoint_descriptor -+hs_status_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .wMaxPacketSize = __constant_cpu_to_le16 (STATUS_BYTECOUNT), -+ .bInterval = LOG2_STATUS_INTERVAL_MSEC + 4, -+}; -+#endif /* DEV_CONFIG_CDC */ -+ -+static struct usb_endpoint_descriptor -+hs_source_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16 (512), -+}; -+ -+static struct usb_endpoint_descriptor -+hs_sink_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16 (512), -+}; -+ -+static struct usb_qualifier_descriptor -+dev_qualifier = { -+ .bLength = sizeof dev_qualifier, -+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, -+ -+ .bcdUSB = __constant_cpu_to_le16 (0x0200), -+ .bDeviceClass = USB_CLASS_COMM, -+ -+ .bNumConfigurations = 1, -+}; -+ -+static const struct usb_descriptor_header *hs_eth_function [11] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+#ifdef DEV_CONFIG_CDC -+ /* "cdc" mode descriptors */ -+ (struct usb_descriptor_header *) &control_intf, -+ (struct usb_descriptor_header *) &header_desc, -+ (struct usb_descriptor_header *) &union_desc, -+ (struct usb_descriptor_header *) ðer_desc, -+ /* NOTE: status endpoint may need to be removed */ -+ (struct usb_descriptor_header *) &hs_status_desc, -+ /* data interface, with altsetting */ -+ (struct usb_descriptor_header *) &data_nop_intf, -+ (struct usb_descriptor_header *) &data_intf, -+ (struct usb_descriptor_header *) &hs_source_desc, -+ (struct usb_descriptor_header *) &hs_sink_desc, -+ NULL, -+#endif /* DEV_CONFIG_CDC */ -+}; -+ -+static inline void __init hs_subset_descriptors(void) -+{ -+#ifdef DEV_CONFIG_SUBSET -+ hs_eth_function[1] = (struct usb_descriptor_header *) &subset_data_intf; -+ hs_eth_function[2] = (struct usb_descriptor_header *) &fs_source_desc; -+ hs_eth_function[3] = (struct usb_descriptor_header *) &fs_sink_desc; -+ hs_eth_function[4] = NULL; -+#else -+ hs_eth_function[1] = NULL; -+#endif -+} -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+static const struct usb_descriptor_header *hs_rndis_function [] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+ /* control interface matches ACM, not Ethernet */ -+ (struct usb_descriptor_header *) &rndis_control_intf, -+ (struct usb_descriptor_header *) &header_desc, -+ (struct usb_descriptor_header *) &call_mgmt_descriptor, -+ (struct usb_descriptor_header *) &acm_descriptor, -+ (struct usb_descriptor_header *) &union_desc, -+ (struct usb_descriptor_header *) &hs_status_desc, -+ /* data interface has no altsetting */ -+ (struct usb_descriptor_header *) &rndis_data_intf, -+ (struct usb_descriptor_header *) &hs_source_desc, -+ (struct usb_descriptor_header *) &hs_sink_desc, -+ NULL, -+}; -+#endif -+ -+ -+/* maxpacket and other transfer characteristics vary by speed. */ -+#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs)) -+ -+#else -+ -+/* if there's no high speed support, maxpacket doesn't change. */ -+#define ep_desc(g,hs,fs) fs -+ -+static inline void __init hs_subset_descriptors(void) -+{ -+} -+ -+#endif /* !CONFIG_USB_GADGET_DUALSPEED */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* descriptors that are built on-demand */ -+ -+static char manufacturer [50]; -+static char product_desc [40] = DRIVER_DESC; -+ -+#ifdef DEV_CONFIG_CDC -+/* address that the host will use ... usually assigned at random */ -+static char ethaddr [2 * ETH_ALEN + 1]; -+#endif -+ -+/* static strings, in UTF-8 */ -+static struct usb_string strings [] = { -+ { STRING_MANUFACTURER, manufacturer, }, -+ { STRING_PRODUCT, product_desc, }, -+ { STRING_DATA, "Ethernet Data", }, -+#ifdef DEV_CONFIG_CDC -+ { STRING_CDC, "CDC Ethernet", }, -+ { STRING_ETHADDR, ethaddr, }, -+ { STRING_CONTROL, "CDC Communications Control", }, -+#endif -+#ifdef DEV_CONFIG_SUBSET -+ { STRING_SUBSET, "CDC Ethernet Subset", }, -+#endif -+#ifdef CONFIG_USB_ETH_RNDIS -+ { STRING_RNDIS, "RNDIS", }, -+ { STRING_RNDIS_CONTROL, "RNDIS Communications Control", }, -+#endif -+ { } /* end of list */ -+}; -+ -+static struct usb_gadget_strings stringtab = { -+ .language = 0x0409, /* en-us */ -+ .strings = strings, -+}; -+ -+/* -+ * one config, two interfaces: control, data. -+ * complications: class descriptors, and an altsetting. -+ */ -+static int -+config_buf (enum usb_device_speed speed, -+ u8 *buf, u8 type, -+ unsigned index, int is_otg) -+{ -+ int len; -+ const struct usb_config_descriptor *config; -+ const struct usb_descriptor_header **function; -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ int hs = (speed == USB_SPEED_HIGH); -+ -+ if (type == USB_DT_OTHER_SPEED_CONFIG) -+ hs = !hs; -+#define which_fn(t) (hs ? hs_ ## t ## _function : fs_ ## t ## _function) -+#else -+#define which_fn(t) (fs_ ## t ## _function) -+#endif -+ -+ if (index >= device_desc.bNumConfigurations) -+ return -EINVAL; -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ /* list the RNDIS config first, to make Microsoft's drivers -+ * happy. DOCSIS 1.0 needs this too. -+ */ -+ if (device_desc.bNumConfigurations == 2 && index == 0) { -+ config = &rndis_config; -+ function = which_fn (rndis); -+ } else -+#endif -+ { -+ config = ð_config; -+ function = which_fn (eth); -+ } -+ -+ /* for now, don't advertise srp-only devices */ -+ if (!is_otg) -+ function++; -+ -+ len = usb_gadget_config_buf (config, buf, USB_BUFSIZ, function); -+ if (len < 0) -+ return len; -+ ((struct usb_config_descriptor *) buf)->bDescriptorType = type; -+ return len; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void eth_start (struct eth_dev *dev, int gfp_flags); -+static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags); -+ -+#ifdef DEV_CONFIG_CDC -+static inline int ether_alt_ep_setup (struct eth_dev *dev, struct usb_ep *ep) -+{ -+ const struct usb_endpoint_descriptor *d; -+ -+ /* With CDC, the host isn't allowed to use these two data -+ * endpoints in the default altsetting for the interface. -+ * so we don't activate them yet. Reset from SET_INTERFACE. -+ * -+ * Strictly speaking RNDIS should work the same: activation is -+ * a side effect of setting a packet filter. Deactivation is -+ * from REMOTE_NDIS_HALT_MSG, reset from REMOTE_NDIS_RESET_MSG. -+ */ -+ -+ /* one endpoint writes data back IN to the host */ -+ if (strcmp (ep->name, EP_IN_NAME) == 0) { -+ d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); -+ ep->driver_data = dev; -+ dev->in = d; -+ -+ /* one endpoint just reads OUT packets */ -+ } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { -+ d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); -+ ep->driver_data = dev; -+ dev->out = d; -+ -+ /* optional status/notification endpoint */ -+ } else if (EP_STATUS_NAME && -+ strcmp (ep->name, EP_STATUS_NAME) == 0) { -+ int result; -+ -+ d = ep_desc (dev->gadget, &hs_status_desc, &fs_status_desc); -+ result = usb_ep_enable (ep, d); -+ if (result < 0) -+ return result; -+ -+ ep->driver_data = dev; -+ dev->status = d; -+ } -+ return 0; -+} -+#endif -+ -+#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) -+static inline int ether_ep_setup (struct eth_dev *dev, struct usb_ep *ep) -+{ -+ int result; -+ const struct usb_endpoint_descriptor *d; -+ -+ /* CDC subset is simpler: if the device is there, -+ * it's live with rx and tx endpoints. -+ * -+ * Do this as a shortcut for RNDIS too. -+ */ -+ -+ /* one endpoint writes data back IN to the host */ -+ if (strcmp (ep->name, EP_IN_NAME) == 0) { -+ d = ep_desc (dev->gadget, &hs_source_desc, &fs_source_desc); -+ result = usb_ep_enable (ep, d); -+ if (result < 0) -+ return result; -+ -+ ep->driver_data = dev; -+ dev->in = d; -+ -+ /* one endpoint just reads OUT packets */ -+ } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { -+ d = ep_desc (dev->gadget, &hs_sink_desc, &fs_sink_desc); -+ result = usb_ep_enable (ep, d); -+ if (result < 0) -+ return result; -+ -+ ep->driver_data = dev; -+ dev->out = d; -+ } -+ -+ return 0; -+} -+#endif -+ -+static int -+set_ether_config (struct eth_dev *dev, int gfp_flags) -+{ -+ int result = 0; -+ struct usb_ep *ep; -+ struct usb_gadget *gadget = dev->gadget; -+ -+ gadget_for_each_ep (ep, gadget) { -+#ifdef DEV_CONFIG_CDC -+ if (!dev->rndis && dev->cdc) { -+ result = ether_alt_ep_setup (dev, ep); -+ if (result == 0) -+ continue; -+ } -+#endif -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ if (dev->rndis && strcmp (ep->name, EP_STATUS_NAME) == 0) { -+ const struct usb_endpoint_descriptor *d; -+ d = ep_desc (gadget, &hs_status_desc, &fs_status_desc); -+ result = usb_ep_enable (ep, d); -+ if (result == 0) { -+ ep->driver_data = dev; -+ dev->status = d; -+ continue; -+ } -+ } else -+#endif -+ -+ { -+#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) -+ result = ether_ep_setup (dev, ep); -+ if (result == 0) -+ continue; -+#endif -+ } -+ -+ /* stop on error */ -+ ERROR (dev, "can't enable %s, result %d\n", ep->name, result); -+ break; -+ } -+ if (!result && (!dev->in_ep || !dev->out_ep)) -+ result = -ENODEV; -+ -+ if (result == 0) -+ result = alloc_requests (dev, qlen (gadget), gfp_flags); -+ -+ /* on error, disable any endpoints */ -+ if (result < 0) { -+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -+ if (dev->status) -+ (void) usb_ep_disable (dev->status_ep); -+#endif -+ dev->status = NULL; -+#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) -+ if (dev->rndis || !dev->cdc) { -+ if (dev->in) -+ (void) usb_ep_disable (dev->in_ep); -+ if (dev->out) -+ (void) usb_ep_disable (dev->out_ep); -+ } -+#endif -+ dev->in = NULL; -+ dev->out = NULL; -+ } else -+ -+ /* activate non-CDC configs right away -+ * this isn't strictly according to the RNDIS spec -+ */ -+#if defined(DEV_CONFIG_SUBSET) || defined(CONFIG_USB_ETH_RNDIS) -+ if (dev->rndis || !dev->cdc) { -+ netif_carrier_on (dev->net); -+ if (netif_running (dev->net)) { -+ spin_unlock (&dev->lock); -+ eth_start (dev, GFP_ATOMIC); -+ spin_lock (&dev->lock); -+ } -+ } -+#endif -+ -+ if (result == 0) -+ DEBUG (dev, "qlen %d\n", qlen (gadget)); -+ -+ /* caller is responsible for cleanup on error */ -+ return result; -+} -+ -+static void eth_reset_config (struct eth_dev *dev) -+{ -+ struct usb_request *req; -+ -+ if (dev->config == 0) -+ return; -+ -+ DEBUG (dev, "%s\n", __FUNCTION__); -+ -+ netif_stop_queue (dev->net); -+ netif_carrier_off (dev->net); -+ -+ /* disable endpoints, forcing (synchronous) completion of -+ * pending i/o. then free the requests. -+ */ -+ if (dev->in) { -+ usb_ep_disable (dev->in_ep); -+ while (likely (!list_empty (&dev->tx_reqs))) { -+ req = container_of (dev->tx_reqs.next, -+ struct usb_request, list); -+ list_del (&req->list); -+ usb_ep_free_request (dev->in_ep, req); -+ } -+ } -+ if (dev->out) { -+ usb_ep_disable (dev->out_ep); -+ while (likely (!list_empty (&dev->rx_reqs))) { -+ req = container_of (dev->rx_reqs.next, -+ struct usb_request, list); -+ list_del (&req->list); -+ usb_ep_free_request (dev->out_ep, req); -+ } -+ } -+ -+ if (dev->status) { -+ usb_ep_disable (dev->status_ep); -+ } -+ dev->config = 0; -+} -+ -+/* change our operational config. must agree with the code -+ * that returns config descriptors, and altsetting code. -+ */ -+static int -+eth_set_config (struct eth_dev *dev, unsigned number, int gfp_flags) -+{ -+ int result = 0; -+ struct usb_gadget *gadget = dev->gadget; -+ -+ if (number == dev->config) -+ return 0; -+ -+ if (gadget_is_sa1100 (gadget) -+ && dev->config -+ && atomic_read (&dev->tx_qlen) != 0) { -+ /* tx fifo is full, but we can't clear it...*/ -+ INFO (dev, "can't change configurations\n"); -+ return -ESPIPE; -+ } -+ eth_reset_config (dev); -+ -+ /* default: pass all packets, no multicast filtering */ -+ dev->cdc_filter = 0x000f; -+ -+ switch (number) { -+ case DEV_CONFIG_VALUE: -+ dev->rndis = 0; -+ result = set_ether_config (dev, gfp_flags); -+ break; -+#ifdef CONFIG_USB_ETH_RNDIS -+ case DEV_RNDIS_CONFIG_VALUE: -+ dev->rndis = 1; -+ result = set_ether_config (dev, gfp_flags); -+ break; -+#endif -+ default: -+ result = -EINVAL; -+ /* FALL THROUGH */ -+ case 0: -+ break; -+ } -+ -+ if (result) { -+ if (number) -+ eth_reset_config (dev); -+ usb_gadget_vbus_draw(dev->gadget, -+ dev->gadget->is_otg ? 8 : 100); -+ } else { -+ char *speed; -+ unsigned power; -+ -+ power = 2 * eth_config.bMaxPower; -+ usb_gadget_vbus_draw(dev->gadget, power); -+ -+ switch (gadget->speed) { -+ case USB_SPEED_FULL: speed = "full"; break; -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ case USB_SPEED_HIGH: speed = "high"; break; -+#endif -+ default: speed = "?"; break; -+ } -+ -+ dev->config = number; -+ INFO (dev, "%s speed config #%d: %d mA, %s, using %s\n", -+ speed, number, power, driver_desc, -+ dev->rndis -+ ? "RNDIS" -+ : (dev->cdc -+ ? "CDC Ethernet" -+ : "CDC Ethernet Subset")); -+ } -+ return result; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef DEV_CONFIG_CDC -+ -+static void eth_status_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct usb_cdc_notification *event = req->buf; -+ int value = req->status; -+ struct eth_dev *dev = ep->driver_data; -+ -+ /* issue the second notification if host reads the first */ -+ if (event->bNotificationType == USB_CDC_NOTIFY_NETWORK_CONNECTION -+ && value == 0) { -+ __le32 *data = req->buf + sizeof *event; -+ -+ event->bmRequestType = 0xA1; -+ event->bNotificationType = USB_CDC_NOTIFY_SPEED_CHANGE; -+ event->wValue = __constant_cpu_to_le16 (0); -+ event->wIndex = __constant_cpu_to_le16 (1); -+ event->wLength = __constant_cpu_to_le16 (8); -+ -+ /* SPEED_CHANGE data is up/down speeds in bits/sec */ -+ data [0] = data [1] = cpu_to_le32 (BITRATE (dev->gadget)); -+ -+ req->length = STATUS_BYTECOUNT; -+ value = usb_ep_queue (ep, req, GFP_ATOMIC); -+ DEBUG (dev, "send SPEED_CHANGE --> %d\n", value); -+ if (value == 0) -+ return; -+ } else if (value != -ECONNRESET) -+ DEBUG (dev, "event %02x --> %d\n", -+ event->bNotificationType, value); -+ event->bmRequestType = 0xff; -+} -+ -+static void issue_start_status (struct eth_dev *dev) -+{ -+ struct usb_request *req = dev->stat_req; -+ struct usb_cdc_notification *event; -+ int value; -+ -+ DEBUG (dev, "%s, flush old status first\n", __FUNCTION__); -+ -+ /* flush old status -+ * -+ * FIXME ugly idiom, maybe we'd be better with just -+ * a "cancel the whole queue" primitive since any -+ * unlink-one primitive has way too many error modes. -+ * here, we "know" toggle is already clear... -+ */ -+ usb_ep_disable (dev->status_ep); -+ usb_ep_enable (dev->status_ep, dev->status); -+ -+ /* 3.8.1 says to issue first NETWORK_CONNECTION, then -+ * a SPEED_CHANGE. could be useful in some configs. -+ */ -+ event = req->buf; -+ event->bmRequestType = 0xA1; -+ event->bNotificationType = USB_CDC_NOTIFY_NETWORK_CONNECTION; -+ event->wValue = __constant_cpu_to_le16 (1); /* connected */ -+ event->wIndex = __constant_cpu_to_le16 (1); -+ event->wLength = 0; -+ -+ req->length = sizeof *event; -+ req->complete = eth_status_complete; -+ value = usb_ep_queue (dev->status_ep, req, GFP_ATOMIC); -+ if (value < 0) -+ DEBUG (dev, "status buf queue --> %d\n", value); -+} -+ -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void eth_setup_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ if (req->status || req->actual != req->length) -+ DEBUG ((struct eth_dev *) ep->driver_data, -+ "setup complete --> %d, %d/%d\n", -+ req->status, req->actual, req->length); -+} -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ -+static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ if (req->status || req->actual != req->length) -+ DEBUG ((struct eth_dev *) ep->driver_data, -+ "rndis response complete --> %d, %d/%d\n", -+ req->status, req->actual, req->length); -+ -+ /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ -+} -+ -+static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct eth_dev *dev = ep->driver_data; -+ int status; -+ -+ /* received RNDIS command from USB_CDC_SEND_ENCAPSULATED_COMMAND */ -+ spin_lock(&dev->lock); -+ status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf); -+ if (status < 0) -+ ERROR(dev, "%s: rndis parse error %d\n", __FUNCTION__, status); -+ spin_unlock(&dev->lock); -+} -+ -+#endif /* RNDIS */ -+ -+/* -+ * The setup() callback implements all the ep0 functionality that's not -+ * handled lower down. CDC has a number of less-common features: -+ * -+ * - two interfaces: control, and ethernet data -+ * - Ethernet data interface has two altsettings: default, and active -+ * - class-specific descriptors for the control interface -+ * - class-specific control requests -+ */ -+static int -+eth_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) -+{ -+ struct eth_dev *dev = get_gadget_data (gadget); -+ struct usb_request *req = dev->req; -+ int value = -EOPNOTSUPP; -+ u16 wIndex = ctrl->wIndex; -+ u16 wValue = ctrl->wValue; -+ u16 wLength = ctrl->wLength; -+ -+ /* descriptors just go into the pre-allocated ep0 buffer, -+ * while config change events may enable network traffic. -+ */ -+ req->complete = eth_setup_complete; -+ switch (ctrl->bRequest) { -+ -+ case USB_REQ_GET_DESCRIPTOR: -+ if (ctrl->bRequestType != USB_DIR_IN) -+ break; -+ switch (wValue >> 8) { -+ -+ case USB_DT_DEVICE: -+ value = min (wLength, (u16) sizeof device_desc); -+ memcpy (req->buf, &device_desc, value); -+ break; -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ case USB_DT_DEVICE_QUALIFIER: -+ if (!gadget->is_dualspeed) -+ break; -+ value = min (wLength, (u16) sizeof dev_qualifier); -+ memcpy (req->buf, &dev_qualifier, value); -+ break; -+ -+ case USB_DT_OTHER_SPEED_CONFIG: -+ if (!gadget->is_dualspeed) -+ break; -+ // FALLTHROUGH -+#endif /* CONFIG_USB_GADGET_DUALSPEED */ -+ case USB_DT_CONFIG: -+ value = config_buf (gadget->speed, req->buf, -+ wValue >> 8, -+ wValue & 0xff, -+ gadget->is_otg); -+ if (value >= 0) -+ value = min (wLength, (u16) value); -+ break; -+ -+ case USB_DT_STRING: -+ value = usb_gadget_get_string (&stringtab, -+ wValue & 0xff, req->buf); -+ if (value >= 0) -+ value = min (wLength, (u16) value); -+ break; -+ } -+ break; -+ -+ case USB_REQ_SET_CONFIGURATION: -+ if (ctrl->bRequestType != 0) -+ break; -+ if (gadget->a_hnp_support) -+ DEBUG (dev, "HNP available\n"); -+ else if (gadget->a_alt_hnp_support) -+ DEBUG (dev, "HNP needs a different root port\n"); -+ spin_lock (&dev->lock); -+ value = eth_set_config (dev, wValue, GFP_ATOMIC); -+ spin_unlock (&dev->lock); -+ break; -+ case USB_REQ_GET_CONFIGURATION: -+ if (ctrl->bRequestType != USB_DIR_IN) -+ break; -+ *(u8 *)req->buf = dev->config; -+ value = min (wLength, (u16) 1); -+ break; -+ -+ case USB_REQ_SET_INTERFACE: -+ if (ctrl->bRequestType != USB_RECIP_INTERFACE -+ || !dev->config -+ || wIndex > 1) -+ break; -+ if (!dev->cdc && wIndex != 0) -+ break; -+ spin_lock (&dev->lock); -+ -+ /* PXA hardware partially handles SET_INTERFACE; -+ * we need to kluge around that interference. -+ */ -+ if (gadget_is_pxa (gadget)) { -+ value = eth_set_config (dev, DEV_CONFIG_VALUE, -+ GFP_ATOMIC); -+ goto done_set_intf; -+ } -+ -+#ifdef DEV_CONFIG_CDC -+ switch (wIndex) { -+ case 0: /* control/master intf */ -+ if (wValue != 0) -+ break; -+ if (dev->status) { -+ usb_ep_disable (dev->status_ep); -+ usb_ep_enable (dev->status_ep, dev->status); -+ } -+ value = 0; -+ break; -+ case 1: /* data intf */ -+ if (wValue > 1) -+ break; -+ usb_ep_disable (dev->in_ep); -+ usb_ep_disable (dev->out_ep); -+ -+ /* CDC requires the data transfers not be done from -+ * the default interface setting ... also, setting -+ * the non-default interface clears filters etc. -+ */ -+ if (wValue == 1) { -+ usb_ep_enable (dev->in_ep, dev->in); -+ usb_ep_enable (dev->out_ep, dev->out); -+ dev->cdc_filter = DEFAULT_FILTER; -+ netif_carrier_on (dev->net); -+ if (dev->status) -+ issue_start_status (dev); -+ if (netif_running (dev->net)) { -+ spin_unlock (&dev->lock); -+ eth_start (dev, GFP_ATOMIC); -+ spin_lock (&dev->lock); -+ } -+ } else { -+ netif_stop_queue (dev->net); -+ netif_carrier_off (dev->net); -+ } -+ value = 0; -+ break; -+ } -+#else -+ /* FIXME this is wrong, as is the assumption that -+ * all non-PXA hardware talks real CDC ... -+ */ -+ WARN(dev, "set_interface ignored!\n"); -+#endif /* DEV_CONFIG_CDC */ -+ -+done_set_intf: -+ spin_unlock (&dev->lock); -+ break; -+ case USB_REQ_GET_INTERFACE: -+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) -+ || !dev->config -+ || wIndex > 1) -+ break; -+ if (!(dev->cdc || dev->rndis) && wIndex != 0) -+ break; -+ -+ /* for CDC, iff carrier is on, data interface is active. */ -+ if (dev->rndis || wIndex != 1) -+ *(u8 *)req->buf = 0; -+ else -+ *(u8 *)req->buf = netif_carrier_ok (dev->net) ? 1 : 0; -+ value = min (wLength, (u16) 1); -+ break; -+ -+#ifdef DEV_CONFIG_CDC -+ case USB_CDC_SET_ETHERNET_PACKET_FILTER: -+ /* see 6.2.30: no data, wIndex = interface, -+ * wValue = packet filter bitmap -+ */ -+ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) -+ || !dev->cdc -+ || dev->rndis -+ || wLength != 0 -+ || wIndex > 1) -+ break; -+ DEBUG (dev, "packet filter %02x\n", wValue); -+ dev->cdc_filter = wValue; -+ value = 0; -+ break; -+ -+ /* and potentially: -+ * case USB_CDC_SET_ETHERNET_MULTICAST_FILTERS: -+ * case USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER: -+ * case USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER: -+ * case USB_CDC_GET_ETHERNET_STATISTIC: -+ */ -+ -+#endif /* DEV_CONFIG_CDC */ -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ /* RNDIS uses the CDC command encapsulation mechanism to implement -+ * an RPC scheme, with much getting/setting of attributes by OID. -+ */ -+ case USB_CDC_SEND_ENCAPSULATED_COMMAND: -+ if (ctrl->bRequestType != (USB_TYPE_CLASS|USB_RECIP_INTERFACE) -+ || !dev->rndis -+ || wLength > USB_BUFSIZ -+ || wValue -+ || rndis_control_intf.bInterfaceNumber -+ != wIndex) -+ break; -+ /* read the request, then process it */ -+ value = wLength; -+ req->complete = rndis_command_complete; -+ /* later, rndis_control_ack () sends a notification */ -+ break; -+ -+ case USB_CDC_GET_ENCAPSULATED_RESPONSE: -+ if ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE) -+ == ctrl->bRequestType -+ && dev->rndis -+ // && wLength >= 0x0400 -+ && !wValue -+ && rndis_control_intf.bInterfaceNumber -+ == wIndex) { -+ u8 *buf; -+ -+ /* return the result */ -+ buf = rndis_get_next_response (dev->rndis_config, -+ &value); -+ if (buf) { -+ memcpy (req->buf, buf, value); -+ req->complete = rndis_response_complete; -+ rndis_free_response(dev->rndis_config, buf); -+ } -+ /* else stalls ... spec says to avoid that */ -+ } -+ break; -+#endif /* RNDIS */ -+ -+ default: -+ VDEBUG (dev, -+ "unknown control req%02x.%02x v%04x i%04x l%d\n", -+ ctrl->bRequestType, ctrl->bRequest, -+ wValue, wIndex, wLength); -+ } -+ -+ /* respond with data transfer before status phase? */ -+ if (value >= 0) { -+ req->length = value; -+ req->zero = value < wLength -+ && (value % gadget->ep0->maxpacket) == 0; -+ value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); -+ if (value < 0) { -+ DEBUG (dev, "ep_queue --> %d\n", value); -+ req->status = 0; -+ eth_setup_complete (gadget->ep0, req); -+ } -+ } -+ -+ /* host either stalls (value < 0) or reports success */ -+ return value; -+} -+ -+static void -+eth_disconnect (struct usb_gadget *gadget) -+{ -+ struct eth_dev *dev = get_gadget_data (gadget); -+ unsigned long flags; -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ netif_stop_queue (dev->net); -+ netif_carrier_off (dev->net); -+ eth_reset_config (dev); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ /* FIXME RNDIS should enter RNDIS_UNINITIALIZED */ -+ -+ /* next we may get setup() calls to enumerate new connections; -+ * or an unbind() during shutdown (including removing module). -+ */ -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* NETWORK DRIVER HOOKUP (to the layer above this driver) */ -+ -+/* glue code: in more recent 2.4 kernels these functions are contained in netdev.h */ -+ -+#ifndef HAVE_NETDEV_PRIV -+static inline void *netdev_priv(struct net_device *net) -+{ -+ return net->priv; -+} -+#endif -+ -+#ifndef HAVE_FREE_NETDEV -+static inline void free_netdev(struct net_device *dev) -+{ -+ kfree(dev); -+} -+#endif -+ -+static int eth_change_mtu (struct net_device *net, int new_mtu) -+{ -+ struct eth_dev *dev = netdev_priv(net); -+ -+ // FIXME if rndis, don't change while link's live -+ -+ if (new_mtu <= ETH_HLEN || new_mtu > ETH_FRAME_LEN) -+ return -ERANGE; -+ /* no zero-length packet read wanted after mtu-sized packets */ -+ if (((new_mtu + sizeof (struct ethhdr)) % dev->in_ep->maxpacket) == 0) -+ return -EDOM; -+ net->mtu = new_mtu; -+ return 0; -+} -+ -+static struct net_device_stats *eth_get_stats (struct net_device *net) -+{ -+ return &((struct eth_dev *)netdev_priv(net))->stats; -+} -+ -+static int eth_ethtool_ioctl (struct net_device *net, void *useraddr) -+{ -+ struct eth_dev *dev = (struct eth_dev *) net->priv; -+ u32 cmd; -+ -+ if (get_user (cmd, (u32 *)useraddr)) -+ return -EFAULT; -+ switch (cmd) { -+ -+ case ETHTOOL_GDRVINFO: { /* get driver info */ -+ struct ethtool_drvinfo info; -+ -+ memset (&info, 0, sizeof info); -+ info.cmd = ETHTOOL_GDRVINFO; -+ strncpy (info.driver, shortname, sizeof info.driver); -+ strncpy (info.version, DRIVER_VERSION, sizeof info.version); -+ strncpy (info.fw_version, dev->gadget->name, sizeof info.fw_version); -+ strncpy (info.bus_info, dev->gadget->dev.bus_id, -+ sizeof info.bus_info); -+ if (copy_to_user (useraddr, &info, sizeof (info))) -+ return -EFAULT; -+ return 0; -+ } -+ -+ case ETHTOOL_GLINK: { /* get link status */ -+ struct ethtool_value edata = { ETHTOOL_GLINK }; -+ -+ edata.data = (dev->gadget->speed != USB_SPEED_UNKNOWN); -+ if (copy_to_user (useraddr, &edata, sizeof (edata))) -+ return -EFAULT; -+ return 0; -+ } -+ -+ } -+ /* Note that the ethtool user space code requires EOPNOTSUPP */ -+ return -EOPNOTSUPP; -+} -+ -+static int eth_ioctl (struct net_device *net, struct ifreq *rq, int cmd) -+{ -+ switch (cmd) { -+ case SIOCETHTOOL: -+ return eth_ethtool_ioctl (net, (void *)rq->ifr_data); -+ default: -+ return -EOPNOTSUPP; -+ } -+} -+ -+static void defer_kevent (struct eth_dev *dev, int flag) -+{ -+ if (test_and_set_bit (flag, &dev->todo)) -+ return; -+ if (!schedule_work (&dev->work)) -+ ERROR (dev, "kevent %d may have been dropped\n", flag); -+ else -+ DEBUG (dev, "kevent %d scheduled\n", flag); -+} -+ -+static void rx_complete (struct usb_ep *ep, struct usb_request *req); -+ -+#ifndef NET_IP_ALIGN -+/* this can be a cpu-specific value */ -+#define NET_IP_ALIGN 2 -+#endif -+ -+static int -+rx_submit (struct eth_dev *dev, struct usb_request *req, int gfp_flags) -+{ -+ struct sk_buff *skb; -+ int retval = -ENOMEM; -+ size_t size; -+ -+ /* Padding up to RX_EXTRA handles minor disagreements with host. -+ * Normally we use the USB "terminate on short read" convention; -+ * so allow up to (N*maxpacket), since that memory is normally -+ * already allocated. Some hardware doesn't deal well with short -+ * reads (e.g. DMA must be N*maxpacket), so for now don't trim a -+ * byte off the end (to force hardware errors on overflow). -+ * -+ * RNDIS uses internal framing, and explicitly allows senders to -+ * pad to end-of-packet. That's potentially nice for speed, -+ * but means receivers can't recover synch on their own. -+ */ -+ size = (sizeof (struct ethhdr) + dev->net->mtu + RX_EXTRA); -+ size += dev->out_ep->maxpacket - 1; -+#ifdef CONFIG_USB_ETH_RNDIS -+ if (dev->rndis) -+ size += sizeof (struct rndis_packet_msg_type); -+#endif -+ size -= size % dev->out_ep->maxpacket; -+ -+ if ((skb = alloc_skb (size + NET_IP_ALIGN, gfp_flags)) == 0) { -+ DEBUG (dev, "no rx skb\n"); -+ goto enomem; -+ } -+ -+ /* Some platforms perform better when IP packets are aligned, -+ * but on at least one, checksumming fails otherwise. Note: -+ * this doesn't account for variable-sized RNDIS headers. -+ */ -+ skb_reserve(skb, NET_IP_ALIGN); -+ -+ req->buf = skb->data; -+ req->length = size; -+ req->complete = rx_complete; -+ req->context = skb; -+ -+ retval = usb_ep_queue (dev->out_ep, req, gfp_flags); -+ if (retval == -ENOMEM) -+enomem: -+ defer_kevent (dev, WORK_RX_MEMORY); -+ if (retval) { -+ DEBUG (dev, "rx submit --> %d\n", retval); -+ dev_kfree_skb_any (skb); -+ spin_lock (&dev->lock); -+ list_add (&req->list, &dev->rx_reqs); -+ spin_unlock (&dev->lock); -+ } -+ return retval; -+} -+ -+static void rx_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct sk_buff *skb = req->context; -+ struct eth_dev *dev = ep->driver_data; -+ int status = req->status; -+ -+ switch (status) { -+ -+ /* normal completion */ -+ case 0: -+ skb_put (skb, req->actual); -+#ifdef CONFIG_USB_ETH_RNDIS -+ /* we know MaxPacketsPerTransfer == 1 here */ -+ if (dev->rndis) -+ rndis_rm_hdr (req->buf, &(skb->len)); -+#endif -+ if (ETH_HLEN > skb->len || skb->len > ETH_FRAME_LEN) { -+ dev->stats.rx_errors++; -+ dev->stats.rx_length_errors++; -+ DEBUG (dev, "rx length %d\n", skb->len); -+ break; -+ } -+ -+ skb->dev = dev->net; -+ skb->protocol = eth_type_trans (skb, dev->net); -+ dev->stats.rx_packets++; -+ dev->stats.rx_bytes += skb->len; -+ -+ /* no buffer copies needed, unless hardware can't -+ * use skb buffers. -+ */ -+ status = netif_rx (skb); -+ skb = NULL; -+ break; -+ -+ /* software-driven interface shutdown */ -+ case -ECONNRESET: // unlink -+ case -ESHUTDOWN: // disconnect etc -+ VDEBUG (dev, "rx shutdown, code %d\n", status); -+ goto quiesce; -+ -+ /* for hardware automagic (such as pxa) */ -+ case -ECONNABORTED: // endpoint reset -+ DEBUG (dev, "rx %s reset\n", ep->name); -+ defer_kevent (dev, WORK_RX_MEMORY); -+quiesce: -+ dev_kfree_skb_any (skb); -+ goto clean; -+ -+ /* data overrun */ -+ case -EOVERFLOW: -+ dev->stats.rx_over_errors++; -+ // FALLTHROUGH -+ -+ default: -+ dev->stats.rx_errors++; -+ DEBUG (dev, "rx status %d\n", status); -+ break; -+ } -+ -+ if (skb) -+ dev_kfree_skb_any (skb); -+ if (!netif_running (dev->net)) { -+clean: -+ /* nobody reading rx_reqs, so no dev->lock */ -+ list_add (&req->list, &dev->rx_reqs); -+ req = NULL; -+ } -+ if (req) -+ rx_submit (dev, req, GFP_ATOMIC); -+} -+ -+static int prealloc (struct list_head *list, struct usb_ep *ep, -+ unsigned n, int gfp_flags) -+{ -+ unsigned i; -+ struct usb_request *req; -+ -+ if (!n) -+ return -ENOMEM; -+ -+ /* queue/recycle up to N requests */ -+ i = n; -+ list_for_each_entry (req, list, list) { -+ if (i-- == 0) -+ goto extra; -+ } -+ while (i--) { -+ req = usb_ep_alloc_request (ep, gfp_flags); -+ if (!req) -+ return list_empty (list) ? -ENOMEM : 0; -+ list_add (&req->list, list); -+ } -+ return 0; -+ -+extra: -+ /* free extras */ -+ for (;;) { -+ struct list_head *next; -+ -+ next = req->list.next; -+ list_del (&req->list); -+ usb_ep_free_request (ep, req); -+ -+ if (next == list) -+ break; -+ -+ req = container_of (next, struct usb_request, list); -+ } -+ return 0; -+} -+ -+static int alloc_requests (struct eth_dev *dev, unsigned n, int gfp_flags) -+{ -+ int status; -+ -+ status = prealloc (&dev->tx_reqs, dev->in_ep, n, gfp_flags); -+ if (status < 0) -+ goto fail; -+ status = prealloc (&dev->rx_reqs, dev->out_ep, n, gfp_flags); -+ if (status < 0) -+ goto fail; -+ return 0; -+fail: -+ DEBUG (dev, "can't alloc requests\n"); -+ return status; -+} -+ -+static void rx_fill (struct eth_dev *dev, int gfp_flags) -+{ -+ struct usb_request *req; -+ unsigned long flags; -+ -+ clear_bit (WORK_RX_MEMORY, &dev->todo); -+ -+ /* fill unused rxq slots with some skb */ -+ spin_lock_irqsave (&dev->lock, flags); -+ while (!list_empty (&dev->rx_reqs)) { -+ req = container_of (dev->rx_reqs.next, -+ struct usb_request, list); -+ list_del_init (&req->list); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ if (rx_submit (dev, req, gfp_flags) < 0) { -+ defer_kevent (dev, WORK_RX_MEMORY); -+ return; -+ } -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ } -+ spin_unlock_irqrestore (&dev->lock, flags); -+} -+ -+static void eth_work (void *_dev) -+{ -+ struct eth_dev *dev = _dev; -+ -+ if (test_bit (WORK_RX_MEMORY, &dev->todo)) { -+ if (netif_running (dev->net)) -+ rx_fill (dev, GFP_KERNEL); -+ else -+ clear_bit (WORK_RX_MEMORY, &dev->todo); -+ } -+ -+ if (dev->todo) -+ DEBUG (dev, "work done, flags = 0x%lx\n", dev->todo); -+} -+ -+static void tx_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct sk_buff *skb = req->context; -+ struct eth_dev *dev = ep->driver_data; -+ -+ switch (req->status) { -+ default: -+ dev->stats.tx_errors++; -+ VDEBUG (dev, "tx err %d\n", req->status); -+ /* FALLTHROUGH */ -+ case -ECONNRESET: // unlink -+ case -ESHUTDOWN: // disconnect etc -+ break; -+ case 0: -+ dev->stats.tx_bytes += skb->len; -+ } -+ dev->stats.tx_packets++; -+ -+ spin_lock (&dev->lock); -+ list_add (&req->list, &dev->tx_reqs); -+ spin_unlock (&dev->lock); -+ dev_kfree_skb_any (skb); -+ -+ atomic_dec (&dev->tx_qlen); -+ if (netif_carrier_ok (dev->net)) -+ netif_wake_queue (dev->net); -+} -+ -+static inline int eth_is_promisc (struct eth_dev *dev) -+{ -+ /* no filters for the CDC subset; always promisc */ -+ if (subset_active (dev)) -+ return 1; -+ return dev->cdc_filter & USB_CDC_PACKET_TYPE_PROMISCUOUS; -+} -+ -+static int eth_start_xmit (struct sk_buff *skb, struct net_device *net) -+{ -+ struct eth_dev *dev = netdev_priv(net); -+ int length = skb->len; -+ int retval; -+ struct usb_request *req = NULL; -+ unsigned long flags; -+ -+ /* apply outgoing CDC or RNDIS filters */ -+ if (!eth_is_promisc (dev)) { -+ u8 *dest = skb->data; -+ -+ if (dest [0] & 0x01) { -+ u16 type; -+ -+ /* ignores USB_CDC_PACKET_TYPE_MULTICAST and host -+ * SET_ETHERNET_MULTICAST_FILTERS requests -+ */ -+ if (memcmp (dest, net->broadcast, ETH_ALEN) == 0) -+ type = USB_CDC_PACKET_TYPE_BROADCAST; -+ else -+ type = USB_CDC_PACKET_TYPE_ALL_MULTICAST; -+ if (!(dev->cdc_filter & type)) { -+ dev_kfree_skb_any (skb); -+ return 0; -+ } -+ } -+ /* ignores USB_CDC_PACKET_TYPE_DIRECTED */ -+ } -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ req = container_of (dev->tx_reqs.next, struct usb_request, list); -+ list_del (&req->list); -+ if (list_empty (&dev->tx_reqs)) -+ netif_stop_queue (net); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ /* no buffer copies needed, unless the network stack did it -+ * or the hardware can't use skb buffers. -+ * or there's not enough space for any RNDIS headers we need -+ */ -+#ifdef CONFIG_USB_ETH_RNDIS -+ if (dev->rndis) { -+ struct sk_buff *skb_rndis; -+ -+ skb_rndis = skb_realloc_headroom (skb, -+ sizeof (struct rndis_packet_msg_type)); -+ if (!skb_rndis) -+ goto drop; -+ -+ dev_kfree_skb_any (skb); -+ skb = skb_rndis; -+ rndis_add_hdr (skb); -+ length = skb->len; -+ } -+#endif -+ req->buf = skb->data; -+ req->context = skb; -+ req->complete = tx_complete; -+ -+ /* use zlp framing on tx for strict CDC-Ether conformance, -+ * though any robust network rx path ignores extra padding. -+ * and some hardware doesn't like to write zlps. -+ */ -+ req->zero = 1; -+ if (!dev->zlp && (length % dev->in_ep->maxpacket) == 0) -+ length++; -+ -+ req->length = length; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ /* throttle highspeed IRQ rate back slightly */ -+ req->no_interrupt = (dev->gadget->speed == USB_SPEED_HIGH) -+ ? ((atomic_read (&dev->tx_qlen) % TX_DELAY) != 0) -+ : 0; -+#endif -+ -+ retval = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC); -+ switch (retval) { -+ default: -+ DEBUG (dev, "tx queue err %d\n", retval); -+ break; -+ case 0: -+ net->trans_start = jiffies; -+ atomic_inc (&dev->tx_qlen); -+ } -+ -+ if (retval) { -+#ifdef CONFIG_USB_ETH_RNDIS -+drop: -+#endif -+ dev->stats.tx_dropped++; -+ dev_kfree_skb_any (skb); -+ spin_lock_irqsave (&dev->lock, flags); -+ if (list_empty (&dev->tx_reqs)) -+ netif_start_queue (net); -+ list_add (&req->list, &dev->tx_reqs); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ } -+ return 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ -+static void rndis_send_media_state (struct eth_dev *dev, int connect) -+{ -+ if (!dev) -+ return; -+ -+ if (connect) { -+ if (rndis_signal_connect (dev->rndis_config)) -+ return; -+ } else { -+ if (rndis_signal_disconnect (dev->rndis_config)) -+ return; -+ } -+} -+ -+static void -+rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ if (req->status || req->actual != req->length) -+ DEBUG ((struct eth_dev *) ep->driver_data, -+ "rndis control ack complete --> %d, %d/%d\n", -+ req->status, req->actual, req->length); -+ -+ usb_ep_free_buffer(ep, req->buf, req->dma, 8); -+ usb_ep_free_request(ep, req); -+} -+ -+static int rndis_control_ack (struct net_device *net) -+{ -+ struct eth_dev *dev = netdev_priv(net); -+ u32 length; -+ struct usb_request *resp; -+ -+ /* in case RNDIS calls this after disconnect */ -+ if (!dev->status_ep) { -+ DEBUG (dev, "status ENODEV\n"); -+ return -ENODEV; -+ } -+ -+ /* Allocate memory for notification ie. ACK */ -+ resp = usb_ep_alloc_request (dev->status_ep, GFP_ATOMIC); -+ if (!resp) { -+ DEBUG (dev, "status ENOMEM\n"); -+ return -ENOMEM; -+ } -+ -+ resp->buf = usb_ep_alloc_buffer (dev->status_ep, 8, -+ &resp->dma, GFP_ATOMIC); -+ if (!resp->buf) { -+ DEBUG (dev, "status buf ENOMEM\n"); -+ usb_ep_free_request (dev->status_ep, resp); -+ return -ENOMEM; -+ } -+ -+ /* Send RNDIS RESPONSE_AVAILABLE notification; -+ * USB_CDC_NOTIFY_RESPONSE_AVAILABLE should work too -+ */ -+ resp->length = 8; -+ resp->complete = rndis_control_ack_complete; -+ -+ *((__le32 *) resp->buf) = __constant_cpu_to_le32 (1); -+ *((__le32 *) resp->buf + 1) = __constant_cpu_to_le32 (0); -+ -+ length = usb_ep_queue (dev->status_ep, resp, GFP_ATOMIC); -+ if (length < 0) { -+ resp->status = 0; -+ rndis_control_ack_complete (dev->status_ep, resp); -+ } -+ -+ return 0; -+} -+ -+#endif /* RNDIS */ -+ -+static void eth_start (struct eth_dev *dev, int gfp_flags) -+{ -+ DEBUG (dev, "%s\n", __FUNCTION__); -+ -+ /* fill the rx queue */ -+ rx_fill (dev, gfp_flags); -+ -+ /* and open the tx floodgates */ -+ atomic_set (&dev->tx_qlen, 0); -+ netif_wake_queue (dev->net); -+#ifdef CONFIG_USB_ETH_RNDIS -+ if (dev->rndis) { -+ rndis_set_param_medium (dev->rndis_config, -+ NDIS_MEDIUM_802_3, -+ BITRATE(dev->gadget)); -+ rndis_send_media_state (dev, 1); -+ } -+#endif -+} -+ -+static int eth_open (struct net_device *net) -+{ -+ struct eth_dev *dev = netdev_priv(net); -+ -+ DEBUG (dev, "%s\n", __FUNCTION__); -+ if (netif_carrier_ok (dev->net)) -+ eth_start (dev, GFP_KERNEL); -+ return 0; -+} -+ -+static int eth_stop (struct net_device *net) -+{ -+ struct eth_dev *dev = netdev_priv(net); -+ -+ VDEBUG (dev, "%s\n", __FUNCTION__); -+ netif_stop_queue (net); -+ -+ DEBUG (dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n", -+ dev->stats.rx_packets, dev->stats.tx_packets, -+ dev->stats.rx_errors, dev->stats.tx_errors -+ ); -+ -+ /* ensure there are no more active requests */ -+ if (dev->config) { -+ usb_ep_disable (dev->in_ep); -+ usb_ep_disable (dev->out_ep); -+ if (netif_carrier_ok (dev->net)) { -+ DEBUG (dev, "host still using in/out endpoints\n"); -+ // FIXME idiom may leave toggle wrong here -+ usb_ep_enable (dev->in_ep, dev->in); -+ usb_ep_enable (dev->out_ep, dev->out); -+ } -+ if (dev->status_ep) { -+ usb_ep_disable (dev->status_ep); -+ usb_ep_enable (dev->status_ep, dev->status); -+ } -+ } -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ if (dev->rndis) { -+ rndis_set_param_medium (dev->rndis_config, -+ NDIS_MEDIUM_802_3, 0); -+ rndis_send_media_state (dev, 0); -+ } -+#endif -+ -+ return 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_request *eth_req_alloc (struct usb_ep *ep, unsigned size) -+{ -+ struct usb_request *req; -+ -+ req = usb_ep_alloc_request (ep, GFP_KERNEL); -+ if (!req) -+ return NULL; -+ -+ req->buf = kmalloc (size, GFP_KERNEL); -+ if (!req->buf) { -+ usb_ep_free_request (ep, req); -+ req = NULL; -+ } -+ return req; -+} -+ -+static void -+eth_req_free (struct usb_ep *ep, struct usb_request *req) -+{ -+ kfree (req->buf); -+ usb_ep_free_request (ep, req); -+} -+ -+ -+static void -+eth_unbind (struct usb_gadget *gadget) -+{ -+ struct eth_dev *dev = get_gadget_data (gadget); -+ -+ DEBUG (dev, "unbind\n"); -+#ifdef CONFIG_USB_ETH_RNDIS -+ rndis_deregister (dev->rndis_config); -+ rndis_exit (); -+#endif -+ -+ /* we've already been disconnected ... no i/o is active */ -+ if (dev->req) { -+ eth_req_free (gadget->ep0, dev->req); -+ dev->req = NULL; -+ } -+ if (dev->stat_req) { -+ eth_req_free (dev->status_ep, dev->stat_req); -+ dev->stat_req = NULL; -+ } -+ -+ unregister_netdev (dev->net); -+ free_netdev(dev->net); -+ -+ /* assuming we used keventd, it must quiesce too */ -+ flush_scheduled_work (); -+ set_gadget_data (gadget, NULL); -+} -+ -+static u8 __init nibble (unsigned char c) -+{ -+ if (likely (isdigit (c))) -+ return c - '0'; -+ c = toupper (c); -+ if (likely (isxdigit (c))) -+ return 10 + c - 'A'; -+ return 0; -+} -+ -+static void __init get_ether_addr (const char *str, u8 *dev_addr) -+{ -+ if (str) { -+ unsigned i; -+ -+ for (i = 0; i < 6; i++) { -+ unsigned char num; -+ -+ if((*str == '.') || (*str == ':')) -+ str++; -+ num = nibble(*str++) << 4; -+ num |= (nibble(*str++)); -+ dev_addr [i] = num; -+ } -+ if (is_valid_ether_addr (dev_addr)) -+ return; -+ } -+ random_ether_addr(dev_addr); -+} -+ -+static int __init -+eth_bind (struct usb_gadget *gadget) -+{ -+ struct eth_dev *dev; -+ struct net_device *net; -+ u8 cdc = 1, zlp = 1, rndis = 1; -+ struct usb_ep *in_ep, *out_ep, *status_ep = NULL; -+ int status = -ENOMEM; -+ -+ /* these flags are only ever cleared; compiler take note */ -+#ifndef DEV_CONFIG_CDC -+ cdc = 0; -+#endif -+#ifndef CONFIG_USB_ETH_RNDIS -+ rndis = 0; -+#endif -+ -+ /* Because most host side USB stacks handle CDC Ethernet, that -+ * standard protocol is _strongly_ preferred for interop purposes. -+ * (By everyone except Microsoft.) -+ */ -+ if (gadget_is_net2280 (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201); -+ } else if (gadget_is_dummy (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0202); -+ } else if (gadget_is_pxa (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203); -+ /* pxa doesn't support altsettings */ -+ cdc = 0; -+ } else if (gadget_is_sh(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204); -+ /* sh doesn't support multiple interfaces or configs */ -+ cdc = 0; -+ rndis = 0; -+ } else if (gadget_is_sa1100 (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205); -+ /* hardware can't write zlps */ -+ zlp = 0; -+ /* sa1100 CAN do CDC, without status endpoint ... we use -+ * non-CDC to be compatible with ARM Linux-2.4 "usb-eth". -+ */ -+ cdc = 0; -+ } else if (gadget_is_goku (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206); -+ } else if (gadget_is_mq11xx (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207); -+ } else if (gadget_is_omap (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); -+ } else if (gadget_is_lh7a40x(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); -+ } else if (gadget_is_n9604(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210); -+ } else if (gadget_is_pxa27x(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211); -+ } else if (gadget_is_s3c2410(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212); -+ } else if (gadget_is_at91(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0213); -+ } else { -+ /* can't assume CDC works. don't want to default to -+ * anything less functional on CDC-capable hardware, -+ * so we fail in this case. -+ */ -+ printk (KERN_ERR "%s: " -+ "controller '%s' not recognized\n", -+ shortname, gadget->name); -+ return -ENODEV; -+ } -+ snprintf (manufacturer, sizeof manufacturer, -+ UTS_SYSNAME " " UTS_RELEASE "/%s", -+ gadget->name); -+ -+ /* If there's an RNDIS configuration, that's what Windows wants to -+ * be using ... so use these product IDs here and in the "linux.inf" -+ * needed to install MSFT drivers. Current Linux kernels will use -+ * the second configuration if it's CDC Ethernet, and need some help -+ * to choose the right configuration otherwise. -+ */ -+ if (rndis) { -+ device_desc.idVendor = -+ __constant_cpu_to_le16(RNDIS_VENDOR_NUM); -+ device_desc.idProduct = -+ __constant_cpu_to_le16(RNDIS_PRODUCT_NUM); -+ snprintf (product_desc, sizeof product_desc, -+ "RNDIS/%s", driver_desc); -+ -+ /* CDC subset ... recognized by Linux since 2.4.10, but Windows -+ * drivers aren't widely available. -+ */ -+ } else if (!cdc) { -+ device_desc.bDeviceClass = USB_CLASS_VENDOR_SPEC; -+ device_desc.idVendor = -+ __constant_cpu_to_le16(SIMPLE_VENDOR_NUM); -+ device_desc.idProduct = -+ __constant_cpu_to_le16(SIMPLE_PRODUCT_NUM); -+ } -+ -+ /* support optional vendor/distro customization */ -+ if (idVendor) { -+ if (!idProduct) { -+ printk (KERN_ERR "%s: idVendor needs idProduct!\n", -+ shortname); -+ return -ENODEV; -+ } -+ device_desc.idVendor = cpu_to_le16(idVendor); -+ device_desc.idProduct = cpu_to_le16(idProduct); -+ if (bcdDevice) -+ device_desc.bcdDevice = cpu_to_le16(bcdDevice); -+ } -+ if (iManufacturer) -+ strncpy (manufacturer, iManufacturer, sizeof manufacturer); -+ if (iProduct) -+ strncpy (product_desc, iProduct, sizeof product_desc); -+ -+ /* all we really need is bulk IN/OUT */ -+ usb_ep_autoconfig_reset (gadget); -+ in_ep = usb_ep_autoconfig (gadget, &fs_source_desc); -+ if (!in_ep) { -+autoconf_fail: -+ printk (KERN_ERR "%s: can't autoconfigure on %s\n", -+ shortname, gadget->name); -+ return -ENODEV; -+ } -+ EP_IN_NAME = in_ep->name; -+ in_ep->driver_data = in_ep; /* claim */ -+ -+ out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc); -+ if (!out_ep) -+ goto autoconf_fail; -+ EP_OUT_NAME = out_ep->name; -+ out_ep->driver_data = out_ep; /* claim */ -+ -+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -+ /* CDC Ethernet control interface doesn't require a status endpoint. -+ * Since some hosts expect one, try to allocate one anyway. -+ */ -+ if (cdc || rndis) { -+ status_ep = usb_ep_autoconfig (gadget, &fs_status_desc); -+ if (status_ep) { -+ EP_STATUS_NAME = status_ep->name; -+ status_ep->driver_data = status_ep; /* claim */ -+ } else if (rndis) { -+ printk (KERN_ERR "%s: " -+ "can't run RNDIS on %s\n", -+ shortname, gadget->name); -+ return -ENODEV; -+#ifdef DEV_CONFIG_CDC -+ /* pxa25x only does CDC subset; often used with RNDIS */ -+ } else if (cdc) { -+ control_intf.bNumEndpoints = 0; -+ /* FIXME remove endpoint from descriptor list */ -+#endif -+ } -+ } -+#endif -+ -+ /* one config: cdc, else minimal subset */ -+ if (!cdc) { -+ eth_config.bNumInterfaces = 1; -+ eth_config.iConfiguration = STRING_SUBSET; -+ fs_subset_descriptors(); -+ hs_subset_descriptors(); -+ } -+ -+ /* For now RNDIS is always a second config */ -+ if (rndis) -+ device_desc.bNumConfigurations = 2; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ if (rndis) -+ dev_qualifier.bNumConfigurations = 2; -+ else if (!cdc) -+ dev_qualifier.bDeviceClass = USB_CLASS_VENDOR_SPEC; -+ -+ /* assumes ep0 uses the same value for both speeds ... */ -+ dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; -+ -+ /* and that all endpoints are dual-speed */ -+ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; -+ hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; -+#if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -+ if (EP_STATUS_NAME) -+ hs_status_desc.bEndpointAddress = -+ fs_status_desc.bEndpointAddress; -+#endif -+#endif /* DUALSPEED */ -+ -+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; -+ usb_gadget_set_selfpowered (gadget); -+ -+ if (gadget->is_otg) { -+ otg_descriptor.bmAttributes |= USB_OTG_HNP, -+ eth_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ eth_config.bMaxPower = 4; -+#ifdef CONFIG_USB_ETH_RNDIS -+ rndis_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ rndis_config.bMaxPower = 4; -+#endif -+ } -+ -+ net = alloc_etherdev (sizeof *dev); -+ if (!net) -+ return status; -+ dev = netdev_priv(net); -+ spin_lock_init (&dev->lock); -+ INIT_WORK (&dev->work, eth_work, dev); -+ INIT_LIST_HEAD (&dev->tx_reqs); -+ INIT_LIST_HEAD (&dev->rx_reqs); -+ -+ /* network device setup */ -+ dev->net = net; -+ SET_MODULE_OWNER (net); -+ strcpy (net->name, "usb%d"); -+ dev->cdc = cdc; -+ dev->zlp = zlp; -+ -+ dev->in_ep = in_ep; -+ dev->out_ep = out_ep; -+ dev->status_ep = status_ep; -+ -+ /* Module params for these addresses should come from ID proms. -+ * The host side address is used with CDC and RNDIS, and commonly -+ * ends up in a persistent config database. -+ */ -+ get_ether_addr(dev_addr, net->dev_addr); -+ if (cdc || rndis) { -+ get_ether_addr(host_addr, dev->host_mac); -+#ifdef DEV_CONFIG_CDC -+ snprintf (ethaddr, sizeof ethaddr, "%02X%02X%02X%02X%02X%02X", -+ dev->host_mac [0], dev->host_mac [1], -+ dev->host_mac [2], dev->host_mac [3], -+ dev->host_mac [4], dev->host_mac [5]); -+#endif -+ } -+ -+ if (rndis) { -+ status = rndis_init(); -+ if (status < 0) { -+ printk (KERN_ERR "%s: can't init RNDIS, %d\n", -+ shortname, status); -+ goto fail; -+ } -+ } -+ -+ net->change_mtu = eth_change_mtu; -+ net->get_stats = eth_get_stats; -+ net->hard_start_xmit = eth_start_xmit; -+ net->open = eth_open; -+ net->stop = eth_stop; -+ // watchdog_timeo, tx_timeout ... -+ // set_multicast_list -+ net->do_ioctl = eth_ioctl; -+ -+ /* preallocate control message data and buffer */ -+ dev->req = eth_req_alloc (gadget->ep0, USB_BUFSIZ); -+ if (!dev->req) -+ goto fail; -+ dev->req->complete = eth_setup_complete; -+ -+ /* PO: this code my be reached with STATUS_BYTECOUNT undefined -+ Don't allocate stat_req then?! -+ */ -+#ifdef STATUS_BYTECOUNT -+ /* ... and maybe likewise for status transfer */ -+ if (dev->status_ep) { -+ dev->stat_req = eth_req_alloc (dev->status_ep, -+ STATUS_BYTECOUNT); -+ if (!dev->stat_req) { -+ eth_req_free (gadget->ep0, dev->req); -+ goto fail; -+ } -+ } -+#endif -+ -+ /* finish hookup to lower layer ... */ -+ dev->gadget = gadget; -+ set_gadget_data (gadget, dev); -+ gadget->ep0->driver_data = dev; -+ -+ /* two kinds of host-initiated state changes: -+ * - iff DATA transfer is active, carrier is "on" -+ * - tx queueing enabled if open *and* carrier is "on" -+ */ -+ netif_stop_queue (dev->net); -+ netif_carrier_off (dev->net); -+ -+ // SET_NETDEV_DEV (dev->net, &gadget->dev); -+ status = register_netdev (dev->net); -+ if (status < 0) -+ goto fail1; -+ -+ INFO (dev, "%s, version: " DRIVER_VERSION "\n", driver_desc); -+ INFO (dev, "using %s, OUT %s IN %s%s%s\n", gadget->name, -+ EP_OUT_NAME, EP_IN_NAME, -+ EP_STATUS_NAME ? " STATUS " : "", -+ EP_STATUS_NAME ? EP_STATUS_NAME : "" -+ ); -+ INFO (dev, "MAC %02x:%02x:%02x:%02x:%02x:%02x\n", -+ net->dev_addr [0], net->dev_addr [1], -+ net->dev_addr [2], net->dev_addr [3], -+ net->dev_addr [4], net->dev_addr [5]); -+ -+ if (cdc || rndis) -+ INFO (dev, "HOST MAC %02x:%02x:%02x:%02x:%02x:%02x\n", -+ dev->host_mac [0], dev->host_mac [1], -+ dev->host_mac [2], dev->host_mac [3], -+ dev->host_mac [4], dev->host_mac [5]); -+ -+#ifdef CONFIG_USB_ETH_RNDIS -+ if (rndis) { -+ u32 vendorID = 0; -+ -+ /* FIXME RNDIS vendor id == "vendor NIC code" == ? */ -+ -+ dev->rndis_config = rndis_register (rndis_control_ack); -+ if (dev->rndis_config < 0) { -+fail0: -+ unregister_netdev (dev->net); -+ status = -ENODEV; -+ goto fail; -+ } -+ -+ /* these set up a lot of the OIDs that RNDIS needs */ -+ rndis_set_host_mac (dev->rndis_config, dev->host_mac); -+ if (rndis_set_param_dev (dev->rndis_config, dev->net, -+ &dev->stats)) -+ goto fail0; -+ if (rndis_set_param_vendor (dev->rndis_config, vendorID, -+ manufacturer)) -+ goto fail0; -+ if (rndis_set_param_medium (dev->rndis_config, -+ NDIS_MEDIUM_802_3, -+ 0)) -+ goto fail0; -+ INFO (dev, "RNDIS ready\n"); -+ } -+#endif -+ -+ return status; -+ -+fail1: -+ DEBUG (dev, "register_netdev failed, %d\n", status); -+fail: -+ eth_unbind (gadget); -+ return status; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void -+eth_suspend (struct usb_gadget *gadget) -+{ -+ struct eth_dev *dev = get_gadget_data (gadget); -+ -+ DEBUG (dev, "suspend\n"); -+ dev->suspended = 1; -+} -+ -+static void -+eth_resume (struct usb_gadget *gadget) -+{ -+ struct eth_dev *dev = get_gadget_data (gadget); -+ -+ DEBUG (dev, "resume\n"); -+ dev->suspended = 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_gadget_driver eth_driver = { -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ .speed = USB_SPEED_HIGH, -+#else -+ .speed = USB_SPEED_FULL, -+#endif -+ .function = (char *) driver_desc, -+ .bind = eth_bind, -+ .unbind = eth_unbind, -+ -+ .setup = eth_setup, -+ .disconnect = eth_disconnect, -+ -+ /* PO: is this available? */ -+ .suspend = eth_suspend, -+ .resume = eth_resume, -+ -+ .driver = { -+ .name = (char *) shortname, -+ // .shutdown = ... -+ // .suspend = ... -+ // .resume = ... -+ }, -+}; -+ -+MODULE_DESCRIPTION (DRIVER_DESC); -+MODULE_AUTHOR ("David Brownell, Benedikt Spanger"); -+MODULE_LICENSE ("GPL"); -+ -+ -+static int __init init (void) -+{ -+ return usb_gadget_register_driver (ð_driver); -+} -+module_init (init); -+ -+static void __exit cleanup (void) -+{ -+ usb_gadget_unregister_driver (ð_driver); -+} -+module_exit (cleanup); -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/file_storage.c kernel/drivers/usb/gadget/file_storage.c ---- /tmp/kernel/drivers/usb/gadget/file_storage.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/file_storage.c 2005-04-22 17:53:19.431540004 +0200 -@@ -0,0 +1,3939 @@ -+/* -+ * file_storage.c -- File-backed USB Storage Gadget, for USB development -+ * -+ * Copyright (C) 2003, 2004 Alan Stern -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+ -+/* -+ * The File-backed Storage Gadget acts as a USB Mass Storage device, -+ * appearing to the host as a disk drive. In addition to providing an -+ * example of a genuinely useful gadget driver for a USB device, it also -+ * illustrates a technique of double-buffering for increased throughput. -+ * Last but not least, it gives an easy way to probe the behavior of the -+ * Mass Storage drivers in a USB host. -+ * -+ * Backing storage is provided by a regular file or a block device, specified -+ * by the "file" module parameter. Access can be limited to read-only by -+ * setting the optional "ro" module parameter. -+ * -+ * The gadget supports the Control-Bulk (CB), Control-Bulk-Interrupt (CBI), -+ * and Bulk-Only (also known as Bulk-Bulk-Bulk or BBB) transports, selected -+ * by the optional "transport" module parameter. It also supports the -+ * following protocols: RBC (0x01), ATAPI or SFF-8020i (0x02), QIC-157 (0c03), -+ * UFI (0x04), SFF-8070i (0x05), and transparent SCSI (0x06), selected by -+ * the optional "protocol" module parameter. For testing purposes the -+ * gadget will indicate that it has removable media if the optional -+ * "removable" module parameter is set. In addition, the default Vendor ID, -+ * Product ID, and release number can be overridden. -+ * -+ * There is support for multiple logical units (LUNs), each of which has -+ * its own backing file. The number of LUNs can be set using the optional -+ * "luns" module parameter (anywhere from 1 to 8), and the corresponding -+ * files are specified using comma-separated lists for "file" and "ro". -+ * The default number of LUNs is taken from the number of "file" elements; -+ * it is 1 if "file" is not given. If "removable" is not set then a backing -+ * file must be specified for each LUN. If it is set, then an unspecified -+ * or empty backing filename means the LUN's medium is not loaded. -+ * -+ * Requirements are modest; only a bulk-in and a bulk-out endpoint are -+ * needed (an interrupt-out endpoint is also needed for CBI). The memory -+ * requirement amounts to two 16K buffers, size configurable by a parameter. -+ * Support is included for both full-speed and high-speed operation. -+ * -+ * Module options: -+ * -+ * file=filename[,filename...] -+ * Required if "removable" is not set, names of -+ * the files or block devices used for -+ * backing storage -+ * ro=b[,b...] Default false, booleans for read-only access -+ * luns=N Default N = number of filenames, number of -+ * LUNs to support -+ * transport=XXX Default BBB, transport name (CB, CBI, or BBB) -+ * protocol=YYY Default SCSI, protocol name (RBC, 8020 or -+ * ATAPI, QIC, UFI, 8070, or SCSI; -+ * also 1 - 6) -+ * removable Default false, boolean for removable media -+ * vendor=0xVVVV Default 0x0525 (NetChip), USB Vendor ID -+ * product=0xPPPP Default 0xa4a5 (FSG), USB Product ID -+ * release=0xRRRR Override the USB release number (bcdDevice) -+ * buflen=N Default N=16384, buffer size used (will be -+ * rounded down to a multiple of -+ * PAGE_CACHE_SIZE) -+ * stall Default determined according to the type of -+ * USB device controller (usually true), -+ * boolean to permit the driver to halt -+ * bulk endpoints -+ * -+ * If CONFIG_USB_FILE_STORAGE_TEST is not set, only the "file" and "ro" -+ * options are available; default values are used for everything else. -+ * -+ * This gadget driver is heavily based on "Gadget Zero" by David Brownell. -+ */ -+ -+ -+/* -+ * Driver Design -+ * -+ * The FSG driver is fairly straightforward. There is a main kernel -+ * thread that handles most of the work. Interrupt routines field -+ * callbacks from the controller driver: bulk- and interrupt-request -+ * completion notifications, endpoint-0 events, and disconnect events. -+ * Completion events are passed to the main thread by wakeup calls. Many -+ * ep0 requests are handled at interrupt time, but SetInterface, -+ * SetConfiguration, and device reset requests are forwarded to the -+ * thread in the form of "exceptions" using SIGUSR1 signals (since they -+ * should interrupt any ongoing file I/O operations). -+ * -+ * The thread's main routine implements the standard command/data/status -+ * parts of a SCSI interaction. It and its subroutines are full of tests -+ * for pending signals/exceptions -- all this polling is necessary since -+ * the kernel has no setjmp/longjmp equivalents. (Maybe this is an -+ * indication that the driver really wants to be running in userspace.) -+ * An important point is that so long as the thread is alive it keeps an -+ * open reference to the backing file. This will prevent unmounting -+ * the backing file's underlying filesystem and could cause problems -+ * during system shutdown, for example. To prevent such problems, the -+ * thread catches INT, TERM, and KILL signals and converts them into -+ * an EXIT exception. -+ * -+ * In normal operation the main thread is started during the gadget's -+ * fsg_bind() callback and stopped during fsg_unbind(). But it can also -+ * exit when it receives a signal, and there's no point leaving the -+ * gadget running when the thread is dead. So just before the thread -+ * exits, it deregisters the gadget driver. This makes things a little -+ * tricky: The driver is deregistered at two places, and the exiting -+ * thread can indirectly call fsg_unbind() which in turn can tell the -+ * thread to exit. The first problem is resolved through the use of the -+ * REGISTERED atomic bitflag; the driver will only be deregistered once. -+ * The second problem is resolved by having fsg_unbind() check -+ * fsg->state; it won't try to stop the thread if the state is already -+ * FSG_STATE_TERMINATED. -+ * -+ * To provide maximum throughput, the driver uses a circular pipeline of -+ * buffer heads (struct fsg_buffhd). In principle the pipeline can be -+ * arbitrarily long; in practice the benefits don't justify having more -+ * than 2 stages (i.e., double buffering). But it helps to think of the -+ * pipeline as being a long one. Each buffer head contains a bulk-in and -+ * a bulk-out request pointer (since the buffer can be used for both -+ * output and input -- directions always are given from the host's -+ * point of view) as well as a pointer to the buffer and various state -+ * variables. -+ * -+ * Use of the pipeline follows a simple protocol. There is a variable -+ * (fsg->next_buffhd_to_fill) that points to the next buffer head to use. -+ * At any time that buffer head may still be in use from an earlier -+ * request, so each buffer head has a state variable indicating whether -+ * it is EMPTY, FULL, or BUSY. Typical use involves waiting for the -+ * buffer head to be EMPTY, filling the buffer either by file I/O or by -+ * USB I/O (during which the buffer head is BUSY), and marking the buffer -+ * head FULL when the I/O is complete. Then the buffer will be emptied -+ * (again possibly by USB I/O, during which it is marked BUSY) and -+ * finally marked EMPTY again (possibly by a completion routine). -+ * -+ * A module parameter tells the driver to avoid stalling the bulk -+ * endpoints wherever the transport specification allows. This is -+ * necessary for some UDCs like the SuperH, which cannot reliably clear a -+ * halt on a bulk endpoint. However, under certain circumstances the -+ * Bulk-only specification requires a stall. In such cases the driver -+ * will halt the endpoint and set a flag indicating that it should clear -+ * the halt in software during the next device reset. Hopefully this -+ * will permit everything to work correctly. -+ * -+ * One subtle point concerns sending status-stage responses for ep0 -+ * requests. Some of these requests, such as device reset, can involve -+ * interrupting an ongoing file I/O operation, which might take an -+ * arbitrarily long time. During that delay the host might give up on -+ * the original ep0 request and issue a new one. When that happens the -+ * driver should not notify the host about completion of the original -+ * request, as the host will no longer be waiting for it. So the driver -+ * assigns to each ep0 request a unique tag, and it keeps track of the -+ * tag value of the request associated with a long-running exception -+ * (device-reset, interface-change, or configuration-change). When the -+ * exception handler is finished, the status-stage response is submitted -+ * only if the current ep0 request tag is equal to the exception request -+ * tag. Thus only the most recently received ep0 request will get a -+ * status-stage response. -+ * -+ * Warning: This driver source file is too long. It ought to be split up -+ * into a header file plus about 3 separate .c files, to handle the details -+ * of the Gadget, USB Mass Storage, and SCSI protocols. -+ */ -+ -+ -+#undef DEBUG -+#undef VERBOSE -+#undef DUMP_MSGS -+ -+#include <linux/config.h> -+ -+#include <asm/system.h> -+#include <asm/uaccess.h> -+ -+#include <linux/bitops.h> -+#include <linux/blkdev.h> -+#include <linux/compiler.h> -+#include <linux/completion.h> -+#include <linux/dcache.h> -+#include <linux/fcntl.h> -+#include <linux/file.h> -+#include <linux/fs.h> -+#include <linux/init.h> -+#include <linux/kernel.h> -+#include <linux/limits.h> -+#include <linux/list.h> -+#include <linux/module.h> -+#include <linux/pagemap.h> -+#include <linux/rwsem.h> -+#include <linux/sched.h> -+#include <linux/signal.h> -+#include <linux/slab.h> -+#include <linux/spinlock.h> -+#include <linux/string.h> -+#include <linux/uts.h> -+#include <linux/version.h> -+#include <linux/wait.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include "gadget_chips.h" -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define DRIVER_DESC "File-backed Storage Gadget" -+#define DRIVER_NAME "g_file_storage" -+#define DRIVER_VERSION "05 June 2004" -+ -+static const char longname[] = DRIVER_DESC; -+static const char shortname[] = DRIVER_NAME; -+ -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_AUTHOR("Alan Stern"); -+MODULE_LICENSE("Dual BSD/GPL"); -+ -+/* Thanks to NetChip Technologies for donating this product ID. -+ * -+ * DO NOT REUSE THESE IDs with any other driver!! Ever!! -+ * Instead: allocate your own, using normal USB-IF procedures. */ -+#define DRIVER_VENDOR_ID 0x0525 // NetChip -+#define DRIVER_PRODUCT_ID 0xa4a5 // Linux-USB File-backed Storage Gadget -+ -+ -+/* -+ * This driver assumes self-powered hardware and has no way for users to -+ * trigger remote wakeup. It uses autoconfiguration to select endpoints -+ * and endpoint addresses. -+ */ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define fakedev_printk(level, dev, format, args...) \ -+ printk(level "%s %s: " format , DRIVER_NAME , (dev)->name , ## args) -+ -+#define xprintk(f,level,fmt,args...) \ -+ fakedev_printk(level , (f)->gadget , fmt , ## args) -+#define yprintk(l,level,fmt,args...) \ -+ fakedev_printk(level , &(l)->dev , fmt , ## args) -+ -+#ifdef DEBUG -+#define DBG(fsg,fmt,args...) \ -+ xprintk(fsg , KERN_DEBUG , fmt , ## args) -+#define LDBG(lun,fmt,args...) \ -+ yprintk(lun , KERN_DEBUG , fmt , ## args) -+#define MDBG(fmt,args...) \ -+ printk(KERN_DEBUG DRIVER_NAME ": " fmt , ## args) -+#else -+#define DBG(fsg,fmt,args...) \ -+ do { } while (0) -+#define LDBG(lun,fmt,args...) \ -+ do { } while (0) -+#define MDBG(fmt,args...) \ -+ do { } while (0) -+#undef VERBOSE -+#undef DUMP_MSGS -+#endif /* DEBUG */ -+ -+#ifdef VERBOSE -+#define VDBG DBG -+#define VLDBG LDBG -+#else -+#define VDBG(fsg,fmt,args...) \ -+ do { } while (0) -+#define VLDBG(lun,fmt,args...) \ -+ do { } while (0) -+#endif /* VERBOSE */ -+ -+#define ERROR(fsg,fmt,args...) \ -+ xprintk(fsg , KERN_ERR , fmt , ## args) -+#define LERROR(lun,fmt,args...) \ -+ yprintk(lun , KERN_ERR , fmt , ## args) -+ -+#define WARN(fsg,fmt,args...) \ -+ xprintk(fsg , KERN_WARNING , fmt , ## args) -+#define LWARN(lun,fmt,args...) \ -+ yprintk(lun , KERN_WARNING , fmt , ## args) -+ -+#define INFO(fsg,fmt,args...) \ -+ xprintk(fsg , KERN_INFO , fmt , ## args) -+#define LINFO(lun,fmt,args...) \ -+ yprintk(lun , KERN_INFO , fmt , ## args) -+ -+#define MINFO(fmt,args...) \ -+ printk(KERN_INFO DRIVER_NAME ": " fmt , ## args) -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Encapsulate the module parameter settings */ -+ -+#define MAX_LUNS 8 -+ -+static char *file[MAX_LUNS] = {NULL, }; -+static int ro[MAX_LUNS] = {0, }; -+static unsigned int luns = 0; -+ // Default values -+static char *transport = "BBB"; -+static char *protocol = "SCSI"; -+static int removable = 0; -+static unsigned short vendor = DRIVER_VENDOR_ID; -+static unsigned short product = DRIVER_PRODUCT_ID; -+static unsigned short release = 0xffff; // Use controller chip type -+static unsigned int buflen = 16384; -+static int stall = 1; -+ -+static struct { -+ unsigned int nluns; -+ -+ char *transport_parm; -+ char *protocol_parm; -+ int removable; -+ unsigned short vendor; -+ unsigned short product; -+ unsigned short release; -+ unsigned int buflen; -+ int can_stall; -+ -+ int transport_type; -+ char *transport_name; -+ int protocol_type; -+ char *protocol_name; -+ -+} mod_data; -+ -+ -+MODULE_PARM(file, "1-8s"); -+MODULE_PARM_DESC(file, "names of backing files or devices"); -+ -+MODULE_PARM(ro, "1-8b"); -+MODULE_PARM_DESC(ro, "true to force read-only"); -+ -+ -+/* In the non-TEST version, only the file and ro module parameters -+ * are available. */ -+#ifdef CONFIG_USB_FILE_STORAGE_TEST -+ -+MODULE_PARM(luns, "i"); -+MODULE_PARM_DESC(luns, "number of LUNs"); -+ -+MODULE_PARM(transport, "s"); -+MODULE_PARM_DESC(transport, "type of transport (BBB, CBI, or CB)"); -+ -+MODULE_PARM(protocol, "s"); -+MODULE_PARM_DESC(protocol, "type of protocol (RBC, 8020, QIC, UFI, " -+ "8070, or SCSI)"); -+ -+MODULE_PARM(removable, "b"); -+MODULE_PARM_DESC(removable, "true to simulate removable media"); -+ -+MODULE_PARM(vendor, "h"); -+MODULE_PARM_DESC(vendor, "USB Vendor ID"); -+ -+MODULE_PARM(product, "h"); -+MODULE_PARM_DESC(product, "USB Product ID"); -+ -+MODULE_PARM(release, "h"); -+MODULE_PARM_DESC(release, "USB release number"); -+ -+MODULE_PARM(buflen, "i"); -+MODULE_PARM_DESC(buflen, "I/O buffer size"); -+ -+MODULE_PARM(stall, "i"); -+MODULE_PARM_DESC(stall, "false to prevent bulk stalls"); -+ -+#endif /* CONFIG_USB_FILE_STORAGE_TEST */ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB protocol value = the transport method */ -+#define USB_PR_CBI 0x00 // Control/Bulk/Interrupt -+#define USB_PR_CB 0x01 // Control/Bulk w/o interrupt -+#define USB_PR_BULK 0x50 // Bulk-only -+ -+/* USB subclass value = the protocol encapsulation */ -+#define USB_SC_RBC 0x01 // Reduced Block Commands (flash) -+#define USB_SC_8020 0x02 // SFF-8020i, MMC-2, ATAPI (CD-ROM) -+#define USB_SC_QIC 0x03 // QIC-157 (tape) -+#define USB_SC_UFI 0x04 // UFI (floppy) -+#define USB_SC_8070 0x05 // SFF-8070i (removable) -+#define USB_SC_SCSI 0x06 // Transparent SCSI -+ -+/* Bulk-only data structures */ -+ -+/* Command Block Wrapper */ -+struct bulk_cb_wrap { -+ u32 Signature; // Contains 'USBC' -+ u32 Tag; // Unique per command id -+ u32 DataTransferLength; // Size of the data -+ u8 Flags; // Direction in bit 7 -+ u8 Lun; // LUN (normally 0) -+ u8 Length; // Of the CDB, <= MAX_COMMAND_SIZE -+ u8 CDB[16]; // Command Data Block -+}; -+ -+#define USB_BULK_CB_WRAP_LEN 31 -+#define USB_BULK_CB_SIG 0x43425355 // Spells out USBC -+#define USB_BULK_IN_FLAG 0x80 -+ -+/* Command Status Wrapper */ -+struct bulk_cs_wrap { -+ u32 Signature; // Should = 'USBS' -+ u32 Tag; // Same as original command -+ u32 Residue; // Amount not transferred -+ u8 Status; // See below -+}; -+ -+#define USB_BULK_CS_WRAP_LEN 13 -+#define USB_BULK_CS_SIG 0x53425355 // Spells out 'USBS' -+#define USB_STATUS_PASS 0 -+#define USB_STATUS_FAIL 1 -+#define USB_STATUS_PHASE_ERROR 2 -+ -+/* Bulk-only class specific requests */ -+#define USB_BULK_RESET_REQUEST 0xff -+#define USB_BULK_GET_MAX_LUN_REQUEST 0xfe -+ -+ -+/* CBI Interrupt data structure */ -+struct interrupt_data { -+ u8 bType; -+ u8 bValue; -+}; -+ -+#define CBI_INTERRUPT_DATA_LEN 2 -+ -+/* CBI Accept Device-Specific Command request */ -+#define USB_CBI_ADSC_REQUEST 0x00 -+ -+ -+#define MAX_COMMAND_SIZE 16 // Length of a SCSI Command Data Block -+ -+/* SCSI commands that we recognize */ -+#define SC_FORMAT_UNIT 0x04 -+#define SC_INQUIRY 0x12 -+#define SC_MODE_SELECT_6 0x15 -+#define SC_MODE_SELECT_10 0x55 -+#define SC_MODE_SENSE_6 0x1a -+#define SC_MODE_SENSE_10 0x5a -+#define SC_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1e -+#define SC_READ_6 0x08 -+#define SC_READ_10 0x28 -+#define SC_READ_12 0xa8 -+#define SC_READ_CAPACITY 0x25 -+#define SC_READ_FORMAT_CAPACITIES 0x23 -+#define SC_RELEASE 0x17 -+#define SC_REQUEST_SENSE 0x03 -+#define SC_RESERVE 0x16 -+#define SC_SEND_DIAGNOSTIC 0x1d -+#define SC_START_STOP_UNIT 0x1b -+#define SC_SYNCHRONIZE_CACHE 0x35 -+#define SC_TEST_UNIT_READY 0x00 -+#define SC_VERIFY 0x2f -+#define SC_WRITE_6 0x0a -+#define SC_WRITE_10 0x2a -+#define SC_WRITE_12 0xaa -+ -+/* SCSI Sense Key/Additional Sense Code/ASC Qualifier values */ -+#define SS_NO_SENSE 0 -+#define SS_COMMUNICATION_FAILURE 0x040800 -+#define SS_INVALID_COMMAND 0x052000 -+#define SS_INVALID_FIELD_IN_CDB 0x052400 -+#define SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE 0x052100 -+#define SS_LOGICAL_UNIT_NOT_SUPPORTED 0x052500 -+#define SS_MEDIUM_NOT_PRESENT 0x023a00 -+#define SS_MEDIUM_REMOVAL_PREVENTED 0x055302 -+#define SS_NOT_READY_TO_READY_TRANSITION 0x062800 -+#define SS_RESET_OCCURRED 0x062900 -+#define SS_SAVING_PARAMETERS_NOT_SUPPORTED 0x053900 -+#define SS_UNRECOVERED_READ_ERROR 0x031100 -+#define SS_WRITE_ERROR 0x030c02 -+#define SS_WRITE_PROTECTED 0x072700 -+ -+#define SK(x) ((u8) ((x) >> 16)) // Sense Key byte, etc. -+#define ASC(x) ((u8) ((x) >> 8)) -+#define ASCQ(x) ((u8) (x)) -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * These definitions will permit the compiler to avoid generating code for -+ * parts of the driver that aren't used in the non-TEST version. Even gcc -+ * can recognize when a test of a constant expression yields a dead code -+ * path. -+ * -+ * Also, in the non-TEST version, open_backing_file() is only used during -+ * initialization and the sysfs attribute store_xxx routines aren't used -+ * at all. We will define NORMALLY_INIT to mark them as __init so they -+ * don't occupy kernel code space unnecessarily. -+ */ -+ -+#ifdef CONFIG_USB_FILE_STORAGE_TEST -+ -+#define transport_is_bbb() (mod_data.transport_type == USB_PR_BULK) -+#define transport_is_cbi() (mod_data.transport_type == USB_PR_CBI) -+#define protocol_is_scsi() (mod_data.protocol_type == USB_SC_SCSI) -+#define backing_file_is_open(curlun) ((curlun)->filp != NULL) -+#define NORMALLY_INIT -+ -+#else -+ -+#define transport_is_bbb() 1 -+#define transport_is_cbi() 0 -+#define protocol_is_scsi() 1 -+#define backing_file_is_open(curlun) 1 -+#define NORMALLY_INIT __init -+ -+#endif /* CONFIG_USB_FILE_STORAGE_TEST */ -+ -+ -+struct lun { -+ struct file *filp; -+ loff_t file_length; -+ loff_t num_sectors; -+ -+ unsigned int ro : 1; -+ unsigned int prevent_medium_removal : 1; -+ unsigned int registered : 1; -+ -+ u32 sense_data; -+ u32 sense_data_info; -+ u32 unit_attention_data; -+ -+#define BUS_ID_SIZE 20 -+ struct __lun_device { -+ char name[BUS_ID_SIZE]; -+ void *driver_data; -+ } dev; -+}; -+ -+ -+/* Big enough to hold our biggest descriptor */ -+#define EP0_BUFSIZE 256 -+#define DELAYED_STATUS (EP0_BUFSIZE + 999) // An impossibly large value -+ -+/* Number of buffers we will use. 2 is enough for double-buffering */ -+#define NUM_BUFFERS 2 -+ -+enum fsg_buffer_state { -+ BUF_STATE_EMPTY = 0, -+ BUF_STATE_FULL, -+ BUF_STATE_BUSY -+}; -+ -+struct fsg_buffhd { -+ void *buf; -+ dma_addr_t dma; -+ volatile enum fsg_buffer_state state; -+ struct fsg_buffhd *next; -+ -+ /* The NetChip 2280 is faster, and handles some protocol faults -+ * better, if we don't submit any short bulk-out read requests. -+ * So we will record the intended request length here. */ -+ unsigned int bulk_out_intended_length; -+ -+ struct usb_request *inreq; -+ volatile int inreq_busy; -+ struct usb_request *outreq; -+ volatile int outreq_busy; -+}; -+ -+enum fsg_state { -+ FSG_STATE_COMMAND_PHASE = -10, // This one isn't used anywhere -+ FSG_STATE_DATA_PHASE, -+ FSG_STATE_STATUS_PHASE, -+ -+ FSG_STATE_IDLE = 0, -+ FSG_STATE_ABORT_BULK_OUT, -+ FSG_STATE_RESET, -+ FSG_STATE_INTERFACE_CHANGE, -+ FSG_STATE_CONFIG_CHANGE, -+ FSG_STATE_DISCONNECT, -+ FSG_STATE_EXIT, -+ FSG_STATE_TERMINATED -+}; -+ -+enum data_direction { -+ DATA_DIR_UNKNOWN = 0, -+ DATA_DIR_FROM_HOST, -+ DATA_DIR_TO_HOST, -+ DATA_DIR_NONE -+}; -+ -+struct fsg_dev { -+ /* lock protects: state, all the req_busy's, and cbbuf_cmnd */ -+ spinlock_t lock; -+ struct usb_gadget *gadget; -+ -+ /* filesem protects: backing files in use */ -+ struct rw_semaphore filesem; -+ -+ struct usb_ep *ep0; // Handy copy of gadget->ep0 -+ struct usb_request *ep0req; // For control responses -+ volatile unsigned int ep0_req_tag; -+ const char *ep0req_name; -+ -+ struct usb_request *intreq; // For interrupt responses -+ volatile int intreq_busy; -+ struct fsg_buffhd *intr_buffhd; -+ -+ unsigned int bulk_out_maxpacket; -+ enum fsg_state state; // For exception handling -+ unsigned int exception_req_tag; -+ -+ u8 config, new_config; -+ -+ unsigned int running : 1; -+ unsigned int bulk_in_enabled : 1; -+ unsigned int bulk_out_enabled : 1; -+ unsigned int intr_in_enabled : 1; -+ unsigned int phase_error : 1; -+ unsigned int short_packet_received : 1; -+ unsigned int bad_lun_okay : 1; -+ -+ unsigned long atomic_bitflags; -+#define REGISTERED 0 -+#define CLEAR_BULK_HALTS 1 -+ -+ struct usb_ep *bulk_in; -+ struct usb_ep *bulk_out; -+ struct usb_ep *intr_in; -+ -+ struct fsg_buffhd *next_buffhd_to_fill; -+ struct fsg_buffhd *next_buffhd_to_drain; -+ struct fsg_buffhd buffhds[NUM_BUFFERS]; -+ -+ wait_queue_head_t thread_wqh; -+ int thread_wakeup_needed; -+ struct completion thread_notifier; -+ int thread_pid; -+ struct task_struct *thread_task; -+ sigset_t thread_signal_mask; -+ -+ int cmnd_size; -+ u8 cmnd[MAX_COMMAND_SIZE]; -+ enum data_direction data_dir; -+ u32 data_size; -+ u32 data_size_from_cmnd; -+ u32 tag; -+ unsigned int lun; -+ u32 residue; -+ u32 usb_amount_left; -+ -+ /* The CB protocol offers no way for a host to know when a command -+ * has completed. As a result the next command may arrive early, -+ * and we will still have to handle it. For that reason we need -+ * a buffer to store new commands when using CB (or CBI, which -+ * does not oblige a host to wait for command completion either). */ -+ int cbbuf_cmnd_size; -+ u8 cbbuf_cmnd[MAX_COMMAND_SIZE]; -+ -+ unsigned int nluns; -+ struct lun *luns; -+ struct lun *curlun; -+}; -+ -+typedef void (*fsg_routine_t)(struct fsg_dev *); -+ -+static int inline exception_in_progress(struct fsg_dev *fsg) -+{ -+ return (fsg->state > FSG_STATE_IDLE); -+} -+ -+/* Make bulk-out requests be divisible by the maxpacket size */ -+static void inline set_bulk_out_req_length(struct fsg_dev *fsg, -+ struct fsg_buffhd *bh, unsigned int length) -+{ -+ unsigned int rem; -+ -+ bh->bulk_out_intended_length = length; -+ rem = length % fsg->bulk_out_maxpacket; -+ if (rem > 0) -+ length += fsg->bulk_out_maxpacket - rem; -+ bh->outreq->length = length; -+} -+ -+static struct fsg_dev *the_fsg; -+static struct usb_gadget_driver fsg_driver; -+ -+static void close_backing_file(struct lun *curlun); -+static void close_all_backing_files(struct fsg_dev *fsg); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef DUMP_MSGS -+ -+static void dump_msg(struct fsg_dev *fsg, const char *label, -+ const u8 *buf, unsigned int length) -+{ -+ unsigned int start, num, i; -+ char line[52], *p; -+ -+ if (length >= 512) -+ return; -+ DBG(fsg, "%s, length %u:\n", label, length); -+ -+ start = 0; -+ while (length > 0) { -+ num = min(length, 16u); -+ p = line; -+ for (i = 0; i < num; ++i) { -+ if (i == 8) -+ *p++ = ' '; -+ sprintf(p, " %02x", buf[i]); -+ p += 3; -+ } -+ *p = 0; -+ printk(KERN_DEBUG "%6x: %s\n", start, line); -+ buf += num; -+ start += num; -+ length -= num; -+ } -+} -+ -+static void inline dump_cdb(struct fsg_dev *fsg) -+{} -+ -+#else -+ -+static void inline dump_msg(struct fsg_dev *fsg, const char *label, -+ const u8 *buf, unsigned int length) -+{} -+ -+static void inline dump_cdb(struct fsg_dev *fsg) -+{ -+ int i; -+ char cmdbuf[3*MAX_COMMAND_SIZE + 1]; -+ -+ for (i = 0; i < fsg->cmnd_size; ++i) -+ sprintf(cmdbuf + i*3, " %02x", fsg->cmnd[i]); -+ VDBG(fsg, "SCSI CDB: %s\n", cmdbuf); -+} -+ -+#endif /* DUMP_MSGS */ -+ -+ -+static int fsg_set_halt(struct fsg_dev *fsg, struct usb_ep *ep) -+{ -+ const char *name; -+ -+ if (ep == fsg->bulk_in) -+ name = "bulk-in"; -+ else if (ep == fsg->bulk_out) -+ name = "bulk-out"; -+ else -+ name = ep->name; -+ DBG(fsg, "%s set halt\n", name); -+ return usb_ep_set_halt(ep); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Routines for unaligned data access */ -+ -+static u16 inline get_be16(u8 *buf) -+{ -+ return ((u16) buf[0] << 8) | ((u16) buf[1]); -+} -+ -+static u32 inline get_be32(u8 *buf) -+{ -+ return ((u32) buf[0] << 24) | ((u32) buf[1] << 16) | -+ ((u32) buf[2] << 8) | ((u32) buf[3]); -+} -+ -+static void inline put_be16(u8 *buf, u16 val) -+{ -+ buf[0] = val >> 8; -+ buf[1] = val; -+} -+ -+static void inline put_be32(u8 *buf, u32 val) -+{ -+ buf[0] = val >> 24; -+ buf[1] = val >> 16; -+ buf[2] = val >> 8; -+ buf[3] = val; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * DESCRIPTORS ... most are static, but strings and (full) configuration -+ * descriptors are built on demand. Also the (static) config and interface -+ * descriptors are adjusted during fsg_bind(). -+ */ -+#define STRING_MANUFACTURER 1 -+#define STRING_PRODUCT 2 -+#define STRING_SERIAL 3 -+ -+/* There is only one configuration. */ -+#define CONFIG_VALUE 1 -+ -+static struct usb_device_descriptor -+device_desc = { -+ .bLength = sizeof device_desc, -+ .bDescriptorType = USB_DT_DEVICE, -+ -+ .bcdUSB = __constant_cpu_to_le16(0x0200), -+ .bDeviceClass = USB_CLASS_PER_INTERFACE, -+ -+ /* The next three values can be overridden by module parameters */ -+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_ID), -+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_ID), -+ .bcdDevice = __constant_cpu_to_le16(0xffff), -+ -+ .iManufacturer = STRING_MANUFACTURER, -+ .iProduct = STRING_PRODUCT, -+ .iSerialNumber = STRING_SERIAL, -+ .bNumConfigurations = 1, -+}; -+ -+static struct usb_config_descriptor -+config_desc = { -+ .bLength = sizeof config_desc, -+ .bDescriptorType = USB_DT_CONFIG, -+ -+ /* wTotalLength computed by usb_gadget_config_buf() */ -+ .bNumInterfaces = 1, -+ .bConfigurationValue = CONFIG_VALUE, -+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -+ .bMaxPower = 1, // self-powered -+}; -+ -+/* There is only one interface. */ -+ -+static struct usb_interface_descriptor -+intf_desc = { -+ .bLength = sizeof intf_desc, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bNumEndpoints = 2, // Adjusted during fsg_bind() -+ .bInterfaceClass = USB_CLASS_MASS_STORAGE, -+ .bInterfaceSubClass = USB_SC_SCSI, // Adjusted during fsg_bind() -+ .bInterfaceProtocol = USB_PR_BULK, // Adjusted during fsg_bind() -+}; -+ -+/* Three full-speed endpoint descriptors: bulk-in, bulk-out, -+ * and interrupt-in. */ -+ -+static struct usb_endpoint_descriptor -+fs_bulk_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ /* wMaxPacketSize set by autoconfiguration */ -+}; -+ -+static struct usb_endpoint_descriptor -+fs_bulk_out_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_OUT, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ /* wMaxPacketSize set by autoconfiguration */ -+}; -+ -+static struct usb_endpoint_descriptor -+fs_intr_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .wMaxPacketSize = __constant_cpu_to_le16(2), -+ .bInterval = 32, // frames -> 32 ms -+}; -+ -+static const struct usb_descriptor_header *fs_function[] = { -+ (struct usb_descriptor_header *) &intf_desc, -+ (struct usb_descriptor_header *) &fs_bulk_in_desc, -+ (struct usb_descriptor_header *) &fs_bulk_out_desc, -+ (struct usb_descriptor_header *) &fs_intr_in_desc, -+ NULL, -+}; -+ -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ -+/* -+ * USB 2.0 devices need to expose both high speed and full speed -+ * descriptors, unless they only run at full speed. -+ * -+ * That means alternate endpoint descriptors (bigger packets) -+ * and a "device qualifier" ... plus more construction options -+ * for the config descriptor. -+ */ -+static struct usb_qualifier_descriptor -+dev_qualifier = { -+ .bLength = sizeof dev_qualifier, -+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, -+ -+ .bcdUSB = __constant_cpu_to_le16(0x0200), -+ .bDeviceClass = USB_CLASS_PER_INTERFACE, -+ -+ .bNumConfigurations = 1, -+}; -+ -+static struct usb_endpoint_descriptor -+hs_bulk_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ /* bEndpointAddress copied from fs_bulk_in_desc during fsg_bind() */ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16(512), -+}; -+ -+static struct usb_endpoint_descriptor -+hs_bulk_out_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ /* bEndpointAddress copied from fs_bulk_out_desc during fsg_bind() */ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16(512), -+ .bInterval = 1, // NAK every 1 uframe -+}; -+ -+static struct usb_endpoint_descriptor -+hs_intr_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ /* bEndpointAddress copied from fs_intr_in_desc during fsg_bind() */ -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .wMaxPacketSize = __constant_cpu_to_le16(2), -+ .bInterval = 9, // 2**(9-1) = 256 uframes -> 32 ms -+}; -+ -+static const struct usb_descriptor_header *hs_function[] = { -+ (struct usb_descriptor_header *) &intf_desc, -+ (struct usb_descriptor_header *) &hs_bulk_in_desc, -+ (struct usb_descriptor_header *) &hs_bulk_out_desc, -+ (struct usb_descriptor_header *) &hs_intr_in_desc, -+ NULL, -+}; -+ -+/* Maxpacket and other transfer characteristics vary by speed. */ -+#define ep_desc(g,fs,hs) (((g)->speed==USB_SPEED_HIGH) ? (hs) : (fs)) -+ -+#else -+ -+/* If there's no high speed support, always use the full-speed descriptor. */ -+#define ep_desc(g,fs,hs) fs -+ -+#endif /* !CONFIG_USB_GADGET_DUALSPEED */ -+ -+ -+/* The CBI specification limits the serial string to 12 uppercase hexadecimal -+ * characters. */ -+static char manufacturer[40]; -+static char serial[13]; -+ -+/* Static strings, in UTF-8 (for simplicity we use only ASCII characters) */ -+static struct usb_string strings[] = { -+ {STRING_MANUFACTURER, manufacturer}, -+ {STRING_PRODUCT, longname}, -+ {STRING_SERIAL, serial}, -+ {} -+}; -+ -+static struct usb_gadget_strings stringtab = { -+ .language = 0x0409, // en-us -+ .strings = strings, -+}; -+ -+ -+/* -+ * Config descriptors must agree with the code that sets configurations -+ * and with code managing interfaces and their altsettings. They must -+ * also handle different speeds and other-speed requests. -+ */ -+static int populate_config_buf(enum usb_device_speed speed, -+ u8 *buf, u8 type, unsigned index) -+{ -+ int len; -+ const struct usb_descriptor_header **function; -+ -+ if (index > 0) -+ return -EINVAL; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ if (type == USB_DT_OTHER_SPEED_CONFIG) -+ speed = (USB_SPEED_FULL + USB_SPEED_HIGH) - speed; -+ if (speed == USB_SPEED_HIGH) -+ function = hs_function; -+ else -+#endif -+ function = fs_function; -+ -+ len = usb_gadget_config_buf(&config_desc, buf, EP0_BUFSIZE, function); -+ if (len < 0) -+ return len; -+ ((struct usb_config_descriptor *) buf)->bDescriptorType = type; -+ return len; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* These routines may be called in process context or in_irq */ -+ -+static void wakeup_thread(struct fsg_dev *fsg) -+{ -+ /* Tell the main thread that something has happened */ -+ fsg->thread_wakeup_needed = 1; -+ wake_up_all(&fsg->thread_wqh); -+} -+ -+ -+static void raise_exception(struct fsg_dev *fsg, enum fsg_state new_state) -+{ -+ unsigned long flags; -+ struct task_struct *thread_task; -+ -+ /* Do nothing if a higher-priority exception is already in progress. -+ * If a lower-or-equal priority exception is in progress, preempt it -+ * and notify the main thread by sending it a signal. */ -+ spin_lock_irqsave(&fsg->lock, flags); -+ if (fsg->state <= new_state) { -+ fsg->exception_req_tag = fsg->ep0_req_tag; -+ fsg->state = new_state; -+ thread_task = fsg->thread_task; -+ if (thread_task) -+ send_sig_info(SIGUSR1, (void *) 1L, thread_task); -+ } -+ spin_unlock_irqrestore(&fsg->lock, flags); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* The disconnect callback and ep0 routines. These always run in_irq, -+ * except that ep0_queue() is called in the main thread to acknowledge -+ * completion of various requests: set config, set interface, and -+ * Bulk-only device reset. */ -+ -+static void fsg_disconnect(struct usb_gadget *gadget) -+{ -+ struct fsg_dev *fsg = get_gadget_data(gadget); -+ -+ DBG(fsg, "disconnect or port reset\n"); -+ raise_exception(fsg, FSG_STATE_DISCONNECT); -+} -+ -+ -+static int ep0_queue(struct fsg_dev *fsg) -+{ -+ int rc; -+ -+ rc = usb_ep_queue(fsg->ep0, fsg->ep0req, GFP_ATOMIC); -+ if (rc != 0 && rc != -ESHUTDOWN) { -+ -+ /* We can't do much more than wait for a reset */ -+ WARN(fsg, "error in submission: %s --> %d\n", -+ fsg->ep0->name, rc); -+ } -+ return rc; -+} -+ -+static void ep0_complete(struct usb_ep *ep, struct usb_request *req) -+{ -+ struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; -+ -+ if (req->actual > 0) -+ dump_msg(fsg, fsg->ep0req_name, req->buf, req->actual); -+ if (req->status || req->actual != req->length) -+ DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__, -+ req->status, req->actual, req->length); -+ if (req->status == -ECONNRESET) // Request was cancelled -+ usb_ep_fifo_flush(ep); -+ -+ if (req->status == 0 && req->context) -+ ((fsg_routine_t) (req->context))(fsg); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Bulk and interrupt endpoint completion handlers. -+ * These always run in_irq. */ -+ -+static void bulk_in_complete(struct usb_ep *ep, struct usb_request *req) -+{ -+ struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; -+ struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; -+ -+ if (req->status || req->actual != req->length) -+ DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__, -+ req->status, req->actual, req->length); -+ if (req->status == -ECONNRESET) // Request was cancelled -+ usb_ep_fifo_flush(ep); -+ -+ /* Hold the lock while we update the request and buffer states */ -+ spin_lock(&fsg->lock); -+ bh->inreq_busy = 0; -+ bh->state = BUF_STATE_EMPTY; -+ spin_unlock(&fsg->lock); -+ wakeup_thread(fsg); -+} -+ -+static void bulk_out_complete(struct usb_ep *ep, struct usb_request *req) -+{ -+ struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; -+ struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; -+ -+ dump_msg(fsg, "bulk-out", req->buf, req->actual); -+ if (req->status || req->actual != bh->bulk_out_intended_length) -+ DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__, -+ req->status, req->actual, -+ bh->bulk_out_intended_length); -+ if (req->status == -ECONNRESET) // Request was cancelled -+ usb_ep_fifo_flush(ep); -+ -+ /* Hold the lock while we update the request and buffer states */ -+ spin_lock(&fsg->lock); -+ bh->outreq_busy = 0; -+ bh->state = BUF_STATE_FULL; -+ spin_unlock(&fsg->lock); -+ wakeup_thread(fsg); -+} -+ -+static void intr_in_complete(struct usb_ep *ep, struct usb_request *req) -+{ -+#ifdef CONFIG_USB_FILE_STORAGE_TEST -+ struct fsg_dev *fsg = (struct fsg_dev *) ep->driver_data; -+ struct fsg_buffhd *bh = (struct fsg_buffhd *) req->context; -+ -+ if (req->status || req->actual != req->length) -+ DBG(fsg, "%s --> %d, %u/%u\n", __FUNCTION__, -+ req->status, req->actual, req->length); -+ if (req->status == -ECONNRESET) // Request was cancelled -+ usb_ep_fifo_flush(ep); -+ -+ /* Hold the lock while we update the request and buffer states */ -+ spin_lock(&fsg->lock); -+ fsg->intreq_busy = 0; -+ bh->state = BUF_STATE_EMPTY; -+ spin_unlock(&fsg->lock); -+ wakeup_thread(fsg); -+#endif /* CONFIG_USB_FILE_STORAGE_TEST */ -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Ep0 class-specific handlers. These always run in_irq. */ -+ -+static void received_cbi_adsc(struct fsg_dev *fsg, struct fsg_buffhd *bh) -+{ -+#ifdef CONFIG_USB_FILE_STORAGE_TEST -+ struct usb_request *req = fsg->ep0req; -+ static u8 cbi_reset_cmnd[6] = { -+ SC_SEND_DIAGNOSTIC, 4, 0xff, 0xff, 0xff, 0xff}; -+ -+ /* Error in command transfer? */ -+ if (req->status || req->length != req->actual || -+ req->actual < 6 || req->actual > MAX_COMMAND_SIZE) { -+ -+ /* Not all controllers allow a protocol stall after -+ * receiving control-out data, but we'll try anyway. */ -+ fsg_set_halt(fsg, fsg->ep0); -+ return; // Wait for reset -+ } -+ -+ /* Is it the special reset command? */ -+ if (req->actual >= sizeof cbi_reset_cmnd && -+ memcmp(req->buf, cbi_reset_cmnd, -+ sizeof cbi_reset_cmnd) == 0) { -+ -+ /* Raise an exception to stop the current operation -+ * and reinitialize our state. */ -+ DBG(fsg, "cbi reset request\n"); -+ raise_exception(fsg, FSG_STATE_RESET); -+ return; -+ } -+ -+ VDBG(fsg, "CB[I] accept device-specific command\n"); -+ spin_lock(&fsg->lock); -+ -+ /* Save the command for later */ -+ if (fsg->cbbuf_cmnd_size) -+ WARN(fsg, "CB[I] overwriting previous command\n"); -+ fsg->cbbuf_cmnd_size = req->actual; -+ memcpy(fsg->cbbuf_cmnd, req->buf, fsg->cbbuf_cmnd_size); -+ -+ spin_unlock(&fsg->lock); -+ wakeup_thread(fsg); -+#endif /* CONFIG_USB_FILE_STORAGE_TEST */ -+} -+ -+ -+static int class_setup_req(struct fsg_dev *fsg, -+ const struct usb_ctrlrequest *ctrl) -+{ -+ struct usb_request *req = fsg->ep0req; -+ int value = -EOPNOTSUPP; -+ -+ if (!fsg->config) -+ return value; -+ -+ /* Handle Bulk-only class-specific requests */ -+ if (transport_is_bbb()) { -+ switch (ctrl->bRequest) { -+ -+ case USB_BULK_RESET_REQUEST: -+ if (ctrl->bRequestType != (USB_DIR_OUT | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE)) -+ break; -+ if (ctrl->wIndex != 0) { -+ value = -EDOM; -+ break; -+ } -+ -+ /* Raise an exception to stop the current operation -+ * and reinitialize our state. */ -+ DBG(fsg, "bulk reset request\n"); -+ raise_exception(fsg, FSG_STATE_RESET); -+ value = DELAYED_STATUS; -+ break; -+ -+ case USB_BULK_GET_MAX_LUN_REQUEST: -+ if (ctrl->bRequestType != (USB_DIR_IN | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE)) -+ break; -+ if (ctrl->wIndex != 0) { -+ value = -EDOM; -+ break; -+ } -+ VDBG(fsg, "get max LUN\n"); -+ *(u8 *) req->buf = fsg->nluns - 1; -+ value = min(ctrl->wLength, (u16) 1); -+ break; -+ } -+ } -+ -+ /* Handle CBI class-specific requests */ -+ else { -+ switch (ctrl->bRequest) { -+ -+ case USB_CBI_ADSC_REQUEST: -+ if (ctrl->bRequestType != (USB_DIR_OUT | -+ USB_TYPE_CLASS | USB_RECIP_INTERFACE)) -+ break; -+ if (ctrl->wIndex != 0) { -+ value = -EDOM; -+ break; -+ } -+ if (ctrl->wLength > MAX_COMMAND_SIZE) { -+ value = -EOVERFLOW; -+ break; -+ } -+ value = ctrl->wLength; -+ fsg->ep0req->context = received_cbi_adsc; -+ break; -+ } -+ } -+ -+ if (value == -EOPNOTSUPP) -+ VDBG(fsg, -+ "unknown class-specific control req " -+ "%02x.%02x v%04x i%04x l%u\n", -+ ctrl->bRequestType, ctrl->bRequest, -+ ctrl->wValue, ctrl->wIndex, ctrl->wLength); -+ return value; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Ep0 standard request handlers. These always run in_irq. */ -+ -+static int standard_setup_req(struct fsg_dev *fsg, -+ const struct usb_ctrlrequest *ctrl) -+{ -+ struct usb_request *req = fsg->ep0req; -+ int value = -EOPNOTSUPP; -+ -+ /* Usually this just stores reply data in the pre-allocated ep0 buffer, -+ * but config change events will also reconfigure hardware. */ -+ switch (ctrl->bRequest) { -+ -+ case USB_REQ_GET_DESCRIPTOR: -+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | -+ USB_RECIP_DEVICE)) -+ break; -+ switch (ctrl->wValue >> 8) { -+ -+ case USB_DT_DEVICE: -+ VDBG(fsg, "get device descriptor\n"); -+ value = min(ctrl->wLength, (u16) sizeof device_desc); -+ memcpy(req->buf, &device_desc, value); -+ break; -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ case USB_DT_DEVICE_QUALIFIER: -+ VDBG(fsg, "get device qualifier\n"); -+ if (!fsg->gadget->is_dualspeed) -+ break; -+ value = min(ctrl->wLength, (u16) sizeof dev_qualifier); -+ memcpy(req->buf, &dev_qualifier, value); -+ break; -+ -+ case USB_DT_OTHER_SPEED_CONFIG: -+ VDBG(fsg, "get other-speed config descriptor\n"); -+ if (!fsg->gadget->is_dualspeed) -+ break; -+ goto get_config; -+#endif -+ case USB_DT_CONFIG: -+ VDBG(fsg, "get configuration descriptor\n"); -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ get_config: -+#endif -+ value = populate_config_buf(fsg->gadget->speed, -+ req->buf, -+ ctrl->wValue >> 8, -+ ctrl->wValue & 0xff); -+ if (value >= 0) -+ value = min(ctrl->wLength, (u16) value); -+ break; -+ -+ case USB_DT_STRING: -+ VDBG(fsg, "get string descriptor\n"); -+ -+ /* wIndex == language code */ -+ value = usb_gadget_get_string(&stringtab, -+ ctrl->wValue & 0xff, req->buf); -+ if (value >= 0) -+ value = min(ctrl->wLength, (u16) value); -+ break; -+ } -+ break; -+ -+ /* One config, two speeds */ -+ case USB_REQ_SET_CONFIGURATION: -+ if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | -+ USB_RECIP_DEVICE)) -+ break; -+ VDBG(fsg, "set configuration\n"); -+ if (ctrl->wValue == CONFIG_VALUE || ctrl->wValue == 0) { -+ fsg->new_config = ctrl->wValue; -+ -+ /* Raise an exception to wipe out previous transaction -+ * state (queued bufs, etc) and set the new config. */ -+ raise_exception(fsg, FSG_STATE_CONFIG_CHANGE); -+ value = DELAYED_STATUS; -+ } -+ break; -+ case USB_REQ_GET_CONFIGURATION: -+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | -+ USB_RECIP_DEVICE)) -+ break; -+ VDBG(fsg, "get configuration\n"); -+ *(u8 *) req->buf = fsg->config; -+ value = min(ctrl->wLength, (u16) 1); -+ break; -+ -+ case USB_REQ_SET_INTERFACE: -+ if (ctrl->bRequestType != (USB_DIR_OUT| USB_TYPE_STANDARD | -+ USB_RECIP_INTERFACE)) -+ break; -+ if (fsg->config && ctrl->wIndex == 0) { -+ -+ /* Raise an exception to wipe out previous transaction -+ * state (queued bufs, etc) and install the new -+ * interface altsetting. */ -+ raise_exception(fsg, FSG_STATE_INTERFACE_CHANGE); -+ value = DELAYED_STATUS; -+ } -+ break; -+ case USB_REQ_GET_INTERFACE: -+ if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | -+ USB_RECIP_INTERFACE)) -+ break; -+ if (!fsg->config) -+ break; -+ if (ctrl->wIndex != 0) { -+ value = -EDOM; -+ break; -+ } -+ VDBG(fsg, "get interface\n"); -+ *(u8 *) req->buf = 0; -+ value = min(ctrl->wLength, (u16) 1); -+ break; -+ -+ default: -+ VDBG(fsg, -+ "unknown control req %02x.%02x v%04x i%04x l%u\n", -+ ctrl->bRequestType, ctrl->bRequest, -+ ctrl->wValue, ctrl->wIndex, ctrl->wLength); -+ } -+ -+ return value; -+} -+ -+ -+static int fsg_setup(struct usb_gadget *gadget, -+ const struct usb_ctrlrequest *ctrl) -+{ -+ struct fsg_dev *fsg = get_gadget_data(gadget); -+ int rc; -+ -+ ++fsg->ep0_req_tag; // Record arrival of a new request -+ fsg->ep0req->context = NULL; -+ fsg->ep0req->length = 0; -+ dump_msg(fsg, "ep0-setup", (u8 *) ctrl, sizeof(*ctrl)); -+ -+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS) -+ rc = class_setup_req(fsg, ctrl); -+ else -+ rc = standard_setup_req(fsg, ctrl); -+ -+ /* Respond with data/status or defer until later? */ -+ if (rc >= 0 && rc != DELAYED_STATUS) { -+ fsg->ep0req->length = rc; -+ fsg->ep0req_name = (ctrl->bRequestType & USB_DIR_IN ? -+ "ep0-in" : "ep0-out"); -+ rc = ep0_queue(fsg); -+ } -+ -+ /* Device either stalls (rc < 0) or reports success */ -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* All the following routines run in process context */ -+ -+ -+/* Use this for bulk or interrupt transfers, not ep0 */ -+static void start_transfer(struct fsg_dev *fsg, struct usb_ep *ep, -+ struct usb_request *req, volatile int *pbusy, -+ volatile enum fsg_buffer_state *state) -+{ -+ int rc; -+ -+ if (ep == fsg->bulk_in) -+ dump_msg(fsg, "bulk-in", req->buf, req->length); -+ else if (ep == fsg->intr_in) -+ dump_msg(fsg, "intr-in", req->buf, req->length); -+ *pbusy = 1; -+ *state = BUF_STATE_BUSY; -+ rc = usb_ep_queue(ep, req, GFP_KERNEL); -+ if (rc != 0) { -+ *pbusy = 0; -+ *state = BUF_STATE_EMPTY; -+ -+ /* We can't do much more than wait for a reset */ -+ -+ /* Note: currently the net2280 driver fails zero-length -+ * submissions if DMA is enabled. */ -+ if (rc != -ESHUTDOWN && !(rc == -EOPNOTSUPP && -+ req->length == 0)) -+ WARN(fsg, "error in submission: %s --> %d\n", -+ ep->name, rc); -+ } -+} -+ -+ -+static int sleep_thread(struct fsg_dev *fsg) -+{ -+ int rc; -+ -+ /* Wait until a signal arrives or we are woken up */ -+ rc = wait_event_interruptible(fsg->thread_wqh, -+ fsg->thread_wakeup_needed); -+ fsg->thread_wakeup_needed = 0; -+ return (rc ? -EINTR : 0); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int do_read(struct fsg_dev *fsg) -+{ -+ struct lun *curlun = fsg->curlun; -+ u32 lba; -+ struct fsg_buffhd *bh; -+ int rc; -+ u32 amount_left; -+ loff_t file_offset, file_offset_tmp; -+ unsigned int amount; -+ unsigned int partial_page; -+ ssize_t nread; -+ -+ /* Get the starting Logical Block Address and check that it's -+ * not too big */ -+ if (fsg->cmnd[0] == SC_READ_6) -+ lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); -+ else { -+ lba = get_be32(&fsg->cmnd[2]); -+ -+ /* We allow DPO (Disable Page Out = don't save data in the -+ * cache) and FUA (Force Unit Access = don't read from the -+ * cache), but we don't implement them. */ -+ if ((fsg->cmnd[1] & ~0x18) != 0) { -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ } -+ if (lba >= curlun->num_sectors) { -+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; -+ return -EINVAL; -+ } -+ file_offset = ((loff_t) lba) << 9; -+ -+ /* Carry out the file reads */ -+ amount_left = fsg->data_size_from_cmnd; -+ if (unlikely(amount_left == 0)) -+ return -EIO; // No default reply -+ -+ for (;;) { -+ -+ /* Figure out how much we need to read: -+ * Try to read the remaining amount. -+ * But don't read more than the buffer size. -+ * And don't try to read past the end of the file. -+ * Finally, if we're not at a page boundary, don't read past -+ * the next page. -+ * If this means reading 0 then we were asked to read past -+ * the end of file. */ -+ amount = min((unsigned int) amount_left, mod_data.buflen); -+ amount = min((loff_t) amount, -+ curlun->file_length - file_offset); -+ partial_page = file_offset & (PAGE_CACHE_SIZE - 1); -+ if (partial_page > 0) -+ amount = min(amount, (unsigned int) PAGE_CACHE_SIZE - -+ partial_page); -+ -+ /* Wait for the next buffer to become available */ -+ bh = fsg->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ -+ /* If we were asked to read past the end of file, -+ * end with an empty buffer. */ -+ if (amount == 0) { -+ curlun->sense_data = -+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; -+ curlun->sense_data_info = file_offset >> 9; -+ bh->inreq->length = 0; -+ bh->state = BUF_STATE_FULL; -+ break; -+ } -+ -+ /* Perform the read */ -+ file_offset_tmp = file_offset; -+ nread = curlun->filp->f_op->read(curlun->filp, -+ (char *) bh->buf, -+ amount, &file_offset_tmp); -+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, -+ (unsigned long long) file_offset, -+ (int) nread); -+ if (signal_pending(current)) -+ return -EINTR; -+ -+ if (nread < 0) { -+ LDBG(curlun, "error in file read: %d\n", -+ (int) nread); -+ nread = 0; -+ } else if (nread < amount) { -+ LDBG(curlun, "partial file read: %d/%u\n", -+ (int) nread, amount); -+ nread -= (nread & 511); // Round down to a block -+ } -+ file_offset += nread; -+ amount_left -= nread; -+ fsg->residue -= nread; -+ bh->inreq->length = nread; -+ bh->state = BUF_STATE_FULL; -+ -+ /* If an error occurred, report it and its position */ -+ if (nread < amount) { -+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR; -+ curlun->sense_data_info = file_offset >> 9; -+ break; -+ } -+ -+ if (amount_left == 0) -+ break; // No more left to read -+ -+ /* Send this buffer and go read some more */ -+ bh->inreq->zero = 0; -+ start_transfer(fsg, fsg->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ fsg->next_buffhd_to_fill = bh->next; -+ } -+ -+ return -EIO; // No default reply -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int do_write(struct fsg_dev *fsg) -+{ -+ struct lun *curlun = fsg->curlun; -+ u32 lba; -+ struct fsg_buffhd *bh; -+ int get_some_more; -+ u32 amount_left_to_req, amount_left_to_write; -+ loff_t usb_offset, file_offset, file_offset_tmp; -+ unsigned int amount; -+ unsigned int partial_page; -+ ssize_t nwritten; -+ int rc; -+ -+ if (curlun->ro) { -+ curlun->sense_data = SS_WRITE_PROTECTED; -+ return -EINVAL; -+ } -+ curlun->filp->f_flags &= ~O_SYNC; // Default is not to wait -+ -+ /* Get the starting Logical Block Address and check that it's -+ * not too big */ -+ if (fsg->cmnd[0] == SC_WRITE_6) -+ lba = (fsg->cmnd[1] << 16) | get_be16(&fsg->cmnd[2]); -+ else { -+ lba = get_be32(&fsg->cmnd[2]); -+ -+ /* We allow DPO (Disable Page Out = don't save data in the -+ * cache) and FUA (Force Unit Access = write directly to the -+ * medium). We don't implement DPO; we implement FUA by -+ * performing synchronous output. */ -+ if ((fsg->cmnd[1] & ~0x18) != 0) { -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ if (fsg->cmnd[1] & 0x08) // FUA -+ curlun->filp->f_flags |= O_SYNC; -+ } -+ if (lba >= curlun->num_sectors) { -+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; -+ return -EINVAL; -+ } -+ -+ /* Carry out the file writes */ -+ get_some_more = 1; -+ file_offset = usb_offset = ((loff_t) lba) << 9; -+ amount_left_to_req = amount_left_to_write = fsg->data_size_from_cmnd; -+ -+ while (amount_left_to_write > 0) { -+ -+ /* Queue a request for more data from the host */ -+ bh = fsg->next_buffhd_to_fill; -+ if (bh->state == BUF_STATE_EMPTY && get_some_more) { -+ -+ /* Figure out how much we want to get: -+ * Try to get the remaining amount. -+ * But don't get more than the buffer size. -+ * And don't try to go past the end of the file. -+ * If we're not at a page boundary, -+ * don't go past the next page. -+ * If this means getting 0, then we were asked -+ * to write past the end of file. -+ * Finally, round down to a block boundary. */ -+ amount = min(amount_left_to_req, mod_data.buflen); -+ amount = min((loff_t) amount, curlun->file_length - -+ usb_offset); -+ partial_page = usb_offset & (PAGE_CACHE_SIZE - 1); -+ if (partial_page > 0) -+ amount = min(amount, -+ (unsigned int) PAGE_CACHE_SIZE - partial_page); -+ -+ if (amount == 0) { -+ get_some_more = 0; -+ curlun->sense_data = -+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; -+ curlun->sense_data_info = usb_offset >> 9; -+ continue; -+ } -+ amount -= (amount & 511); -+ if (amount == 0) { -+ -+ /* Why were we were asked to transfer a -+ * partial block? */ -+ get_some_more = 0; -+ continue; -+ } -+ -+ /* Get the next buffer */ -+ usb_offset += amount; -+ fsg->usb_amount_left -= amount; -+ amount_left_to_req -= amount; -+ if (amount_left_to_req == 0) -+ get_some_more = 0; -+ -+ /* amount is always divisible by 512, hence by -+ * the bulk-out maxpacket size */ -+ bh->outreq->length = bh->bulk_out_intended_length = -+ amount; -+ start_transfer(fsg, fsg->bulk_out, bh->outreq, -+ &bh->outreq_busy, &bh->state); -+ fsg->next_buffhd_to_fill = bh->next; -+ continue; -+ } -+ -+ /* Write the received data to the backing file */ -+ bh = fsg->next_buffhd_to_drain; -+ if (bh->state == BUF_STATE_EMPTY && !get_some_more) -+ break; // We stopped early -+ if (bh->state == BUF_STATE_FULL) { -+ fsg->next_buffhd_to_drain = bh->next; -+ bh->state = BUF_STATE_EMPTY; -+ -+ /* Did something go wrong with the transfer? */ -+ if (bh->outreq->status != 0) { -+ curlun->sense_data = SS_COMMUNICATION_FAILURE; -+ curlun->sense_data_info = file_offset >> 9; -+ break; -+ } -+ -+ amount = bh->outreq->actual; -+ if (curlun->file_length - file_offset < amount) { -+ LERROR(curlun, -+ "write %u @ %llu beyond end %llu\n", -+ amount, (unsigned long long) file_offset, -+ (unsigned long long) curlun->file_length); -+ amount = curlun->file_length - file_offset; -+ } -+ -+ /* Perform the write */ -+ file_offset_tmp = file_offset; -+ nwritten = curlun->filp->f_op->write(curlun->filp, -+ (char *) bh->buf, -+ amount, &file_offset_tmp); -+ VLDBG(curlun, "file write %u @ %llu -> %d\n", amount, -+ (unsigned long long) file_offset, -+ (int) nwritten); -+ if (signal_pending(current)) -+ return -EINTR; // Interrupted! -+ -+ if (nwritten < 0) { -+ LDBG(curlun, "error in file write: %d\n", -+ (int) nwritten); -+ nwritten = 0; -+ } else if (nwritten < amount) { -+ LDBG(curlun, "partial file write: %d/%u\n", -+ (int) nwritten, amount); -+ nwritten -= (nwritten & 511); -+ // Round down to a block -+ } -+ file_offset += nwritten; -+ amount_left_to_write -= nwritten; -+ fsg->residue -= nwritten; -+ -+ /* If an error occurred, report it and its position */ -+ if (nwritten < amount) { -+ curlun->sense_data = SS_WRITE_ERROR; -+ curlun->sense_data_info = file_offset >> 9; -+ break; -+ } -+ -+ /* Did the host decide to stop early? */ -+ if (bh->outreq->actual != bh->outreq->length) { -+ fsg->short_packet_received = 1; -+ break; -+ } -+ continue; -+ } -+ -+ /* Wait for something to happen */ -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ -+ return -EIO; // No default reply -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Sync the file data, don't bother with the metadata. -+ * This code was copied from fs/buffer.c:sys_fdatasync(). */ -+static int fsync_sub(struct lun *curlun) -+{ -+ struct file *filp = curlun->filp; -+ struct inode *inode; -+ int rc, err; -+ -+ if (curlun->ro || !filp) -+ return 0; -+ if (!filp->f_op->fsync) -+ return -EINVAL; -+ -+ inode = filp->f_dentry->d_inode; -+ down(&inode->i_sem); -+ rc = filemap_fdatasync(inode->i_mapping); -+ err = filp->f_op->fsync(filp, filp->f_dentry, 1); -+ if (!rc) -+ rc = err; -+ err = filemap_fdatawait(inode->i_mapping); -+ if (!rc) -+ rc = err; -+ up(&inode->i_sem); -+ VLDBG(curlun, "fdatasync -> %d\n", rc); -+ return rc; -+} -+ -+static void fsync_all(struct fsg_dev *fsg) -+{ -+ int i; -+ -+ for (i = 0; i < fsg->nluns; ++i) -+ fsync_sub(&fsg->luns[i]); -+} -+ -+static int do_synchronize_cache(struct fsg_dev *fsg) -+{ -+ struct lun *curlun = fsg->curlun; -+ int rc; -+ -+ /* We ignore the requested LBA and write out all file's -+ * dirty data buffers. */ -+ rc = fsync_sub(curlun); -+ if (rc) -+ curlun->sense_data = SS_WRITE_ERROR; -+ return 0; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void invalidate_sub(struct lun *curlun) -+{ -+ struct file *filp = curlun->filp; -+ struct inode *inode = filp->f_dentry->d_inode; -+ -+ invalidate_inode_pages(inode); -+} -+ -+static int do_verify(struct fsg_dev *fsg) -+{ -+ struct lun *curlun = fsg->curlun; -+ u32 lba; -+ u32 verification_length; -+ struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; -+ loff_t file_offset, file_offset_tmp; -+ u32 amount_left; -+ unsigned int amount; -+ ssize_t nread; -+ -+ /* Get the starting Logical Block Address and check that it's -+ * not too big */ -+ lba = get_be32(&fsg->cmnd[2]); -+ if (lba >= curlun->num_sectors) { -+ curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; -+ return -EINVAL; -+ } -+ -+ /* We allow DPO (Disable Page Out = don't save data in the -+ * cache) but we don't implement it. */ -+ if ((fsg->cmnd[1] & ~0x10) != 0) { -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ -+ verification_length = get_be16(&fsg->cmnd[7]); -+ if (unlikely(verification_length == 0)) -+ return -EIO; // No default reply -+ -+ /* Prepare to carry out the file verify */ -+ amount_left = verification_length << 9; -+ file_offset = ((loff_t) lba) << 9; -+ -+ /* Write out all the dirty buffers before invalidating them */ -+ fsync_sub(curlun); -+ if (signal_pending(current)) -+ return -EINTR; -+ -+ invalidate_sub(curlun); -+ if (signal_pending(current)) -+ return -EINTR; -+ -+ /* Just try to read the requested blocks */ -+ while (amount_left > 0) { -+ -+ /* Figure out how much we need to read: -+ * Try to read the remaining amount, but not more than -+ * the buffer size. -+ * And don't try to read past the end of the file. -+ * If this means reading 0 then we were asked to read -+ * past the end of file. */ -+ amount = min((unsigned int) amount_left, mod_data.buflen); -+ amount = min((loff_t) amount, -+ curlun->file_length - file_offset); -+ if (amount == 0) { -+ curlun->sense_data = -+ SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE; -+ curlun->sense_data_info = file_offset >> 9; -+ break; -+ } -+ -+ /* Perform the read */ -+ file_offset_tmp = file_offset; -+ nread = curlun->filp->f_op->read(curlun->filp, -+ (char *) bh->buf, -+ amount, &file_offset_tmp); -+ VLDBG(curlun, "file read %u @ %llu -> %d\n", amount, -+ (unsigned long long) file_offset, -+ (int) nread); -+ if (signal_pending(current)) -+ return -EINTR; -+ -+ if (nread < 0) { -+ LDBG(curlun, "error in file verify: %d\n", -+ (int) nread); -+ nread = 0; -+ } else if (nread < amount) { -+ LDBG(curlun, "partial file verify: %d/%u\n", -+ (int) nread, amount); -+ nread -= (nread & 511); // Round down to a sector -+ } -+ if (nread == 0) { -+ curlun->sense_data = SS_UNRECOVERED_READ_ERROR; -+ curlun->sense_data_info = file_offset >> 9; -+ break; -+ } -+ file_offset += nread; -+ amount_left -= nread; -+ } -+ return 0; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int do_inquiry(struct fsg_dev *fsg, struct fsg_buffhd *bh) -+{ -+ u8 *buf = (u8 *) bh->buf; -+ -+ static char vendor_id[] = "Linux "; -+ static char product_id[] = "File-Stor Gadget"; -+ -+ if (!fsg->curlun) { // Unsupported LUNs are okay -+ fsg->bad_lun_okay = 1; -+ memset(buf, 0, 36); -+ buf[0] = 0x7f; // Unsupported, no device-type -+ return 36; -+ } -+ -+ memset(buf, 0, 8); // Non-removable, direct-access device -+ if (mod_data.removable) -+ buf[1] = 0x80; -+ buf[2] = 2; // ANSI SCSI level 2 -+ buf[3] = 2; // SCSI-2 INQUIRY data format -+ buf[4] = 31; // Additional length -+ // No special options -+ sprintf(buf + 8, "%-8s%-16s%04x", vendor_id, product_id, -+ mod_data.release); -+ return 36; -+} -+ -+ -+static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) -+{ -+ struct lun *curlun = fsg->curlun; -+ u8 *buf = (u8 *) bh->buf; -+ u32 sd, sdinfo; -+ -+ /* -+ * From the SCSI-2 spec., section 7.9 (Unit attention condition): -+ * -+ * If a REQUEST SENSE command is received from an initiator -+ * with a pending unit attention condition (before the target -+ * generates the contingent allegiance condition), then the -+ * target shall either: -+ * a) report any pending sense data and preserve the unit -+ * attention condition on the logical unit, or, -+ * b) report the unit attention condition, may discard any -+ * pending sense data, and clear the unit attention -+ * condition on the logical unit for that initiator. -+ * -+ * FSG normally uses option a); enable this code to use option b). -+ */ -+#if 0 -+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE) { -+ curlun->sense_data = curlun->unit_attention_data; -+ curlun->unit_attention_data = SS_NO_SENSE; -+ } -+#endif -+ -+ if (!curlun) { // Unsupported LUNs are okay -+ fsg->bad_lun_okay = 1; -+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; -+ sdinfo = 0; -+ } else { -+ sd = curlun->sense_data; -+ sdinfo = curlun->sense_data_info; -+ curlun->sense_data = SS_NO_SENSE; -+ curlun->sense_data_info = 0; -+ } -+ -+ memset(buf, 0, 18); -+ buf[0] = 0x80 | 0x70; // Valid, current error -+ buf[2] = SK(sd); -+ put_be32(&buf[3], sdinfo); // Sense information -+ buf[7] = 18 - 8; // Additional sense length -+ buf[12] = ASC(sd); -+ buf[13] = ASCQ(sd); -+ return 18; -+} -+ -+ -+static int do_read_capacity(struct fsg_dev *fsg, struct fsg_buffhd *bh) -+{ -+ struct lun *curlun = fsg->curlun; -+ u32 lba = get_be32(&fsg->cmnd[2]); -+ int pmi = fsg->cmnd[8]; -+ u8 *buf = (u8 *) bh->buf; -+ -+ /* Check the PMI and LBA fields */ -+ if (pmi > 1 || (pmi == 0 && lba != 0)) { -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ -+ put_be32(&buf[0], curlun->num_sectors - 1); // Max logical block -+ put_be32(&buf[4], 512); // Block length -+ return 8; -+} -+ -+ -+static int do_mode_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh) -+{ -+ struct lun *curlun = fsg->curlun; -+ int mscmnd = fsg->cmnd[0]; -+ u8 *buf = (u8 *) bh->buf; -+ u8 *buf0 = buf; -+ int pc, page_code; -+ int changeable_values, all_pages; -+ int valid_page = 0; -+ int len, limit; -+ -+ if ((fsg->cmnd[1] & ~0x08) != 0) { // Mask away DBD -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ pc = fsg->cmnd[2] >> 6; -+ page_code = fsg->cmnd[2] & 0x3f; -+ if (pc == 3) { -+ curlun->sense_data = SS_SAVING_PARAMETERS_NOT_SUPPORTED; -+ return -EINVAL; -+ } -+ changeable_values = (pc == 1); -+ all_pages = (page_code == 0x3f); -+ -+ /* Write the mode parameter header. Fixed values are: default -+ * medium type, no cache control (DPOFUA), and no block descriptors. -+ * The only variable value is the WriteProtect bit. We will fill in -+ * the mode data length later. */ -+ memset(buf, 0, 8); -+ if (mscmnd == SC_MODE_SENSE_6) { -+ buf[2] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA -+ buf += 4; -+ limit = 255; -+ } else { // SC_MODE_SENSE_10 -+ buf[3] = (curlun->ro ? 0x80 : 0x00); // WP, DPOFUA -+ buf += 8; -+ limit = 65535; // Should really be mod_data.buflen -+ } -+ -+ /* No block descriptors */ -+ -+ /* The mode pages, in numerical order. The only page we support -+ * is the Caching page. */ -+ if (page_code == 0x08 || all_pages) { -+ valid_page = 1; -+ buf[0] = 0x08; // Page code -+ buf[1] = 10; // Page length -+ memset(buf+2, 0, 10); // None of the fields are changeable -+ -+ if (!changeable_values) { -+ buf[2] = 0x04; // Write cache enable, -+ // Read cache not disabled -+ // No cache retention priorities -+ put_be16(&buf[4], 0xffff); // Don't disable prefetch -+ // Minimum prefetch = 0 -+ put_be16(&buf[8], 0xffff); // Maximum prefetch -+ put_be16(&buf[10], 0xffff); // Maximum prefetch ceiling -+ } -+ buf += 12; -+ } -+ -+ /* Check that a valid page was requested and the mode data length -+ * isn't too long. */ -+ len = buf - buf0; -+ if (!valid_page || len > limit) { -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ -+ /* Store the mode data length */ -+ if (mscmnd == SC_MODE_SENSE_6) -+ buf0[0] = len - 1; -+ else -+ put_be16(buf0, len - 2); -+ return len; -+} -+ -+ -+static int do_start_stop(struct fsg_dev *fsg) -+{ -+ struct lun *curlun = fsg->curlun; -+ int loej, start; -+ -+ if (!mod_data.removable) { -+ curlun->sense_data = SS_INVALID_COMMAND; -+ return -EINVAL; -+ } -+ -+ // int immed = fsg->cmnd[1] & 0x01; -+ loej = fsg->cmnd[4] & 0x02; -+ start = fsg->cmnd[4] & 0x01; -+ -+#ifdef CONFIG_USB_FILE_STORAGE_TEST -+ if ((fsg->cmnd[1] & ~0x01) != 0 || // Mask away Immed -+ (fsg->cmnd[4] & ~0x03) != 0) { // Mask LoEj, Start -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ -+ if (!start) { -+ -+ /* Are we allowed to unload the media? */ -+ if (curlun->prevent_medium_removal) { -+ LDBG(curlun, "unload attempt prevented\n"); -+ curlun->sense_data = SS_MEDIUM_REMOVAL_PREVENTED; -+ return -EINVAL; -+ } -+ if (loej) { // Simulate an unload/eject -+ up_read(&fsg->filesem); -+ down_write(&fsg->filesem); -+ close_backing_file(curlun); -+ up_write(&fsg->filesem); -+ down_read(&fsg->filesem); -+ } -+ } else { -+ -+ /* Our emulation doesn't support mounting; the medium is -+ * available for use as soon as it is loaded. */ -+ if (!backing_file_is_open(curlun)) { -+ curlun->sense_data = SS_MEDIUM_NOT_PRESENT; -+ return -EINVAL; -+ } -+ } -+#endif -+ return 0; -+} -+ -+ -+static int do_prevent_allow(struct fsg_dev *fsg) -+{ -+ struct lun *curlun = fsg->curlun; -+ int prevent; -+ -+ if (!mod_data.removable) { -+ curlun->sense_data = SS_INVALID_COMMAND; -+ return -EINVAL; -+ } -+ -+ prevent = fsg->cmnd[4] & 0x01; -+ if ((fsg->cmnd[4] & ~0x01) != 0) { // Mask away Prevent -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ -+ if (curlun->prevent_medium_removal && !prevent) -+ fsync_sub(curlun); -+ curlun->prevent_medium_removal = prevent; -+ return 0; -+} -+ -+ -+static int do_read_format_capacities(struct fsg_dev *fsg, -+ struct fsg_buffhd *bh) -+{ -+ struct lun *curlun = fsg->curlun; -+ u8 *buf = (u8 *) bh->buf; -+ -+ buf[0] = buf[1] = buf[2] = 0; -+ buf[3] = 8; // Only the Current/Maximum Capacity Descriptor -+ buf += 4; -+ -+ put_be32(&buf[0], curlun->num_sectors); // Number of blocks -+ put_be32(&buf[4], 512); // Block length -+ buf[4] = 0x02; // Current capacity -+ return 12; -+} -+ -+ -+static int do_mode_select(struct fsg_dev *fsg, struct fsg_buffhd *bh) -+{ -+ struct lun *curlun = fsg->curlun; -+ -+ /* We don't support MODE SELECT */ -+ curlun->sense_data = SS_INVALID_COMMAND; -+ return -EINVAL; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int halt_bulk_in_endpoint(struct fsg_dev *fsg) -+{ -+ int rc; -+ -+ rc = fsg_set_halt(fsg, fsg->bulk_in); -+ if (rc == -EAGAIN) -+ VDBG(fsg, "delayed bulk-in endpoint halt\n"); -+ while (rc != 0) { -+ if (rc != -EAGAIN) { -+ WARN(fsg, "usb_ep_set_halt -> %d\n", rc); -+ rc = 0; -+ break; -+ } -+ -+ /* Wait for a short time and then try again */ -+ set_current_state(TASK_INTERRUPTIBLE); -+ if (schedule_timeout(HZ / 10) != 0) -+ return -EINTR; -+ rc = usb_ep_set_halt(fsg->bulk_in); -+ } -+ return rc; -+} -+ -+static int pad_with_zeros(struct fsg_dev *fsg) -+{ -+ struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; -+ u32 nkeep = bh->inreq->length; -+ u32 nsend; -+ int rc; -+ -+ bh->state = BUF_STATE_EMPTY; // For the first iteration -+ fsg->usb_amount_left = nkeep + fsg->residue; -+ while (fsg->usb_amount_left > 0) { -+ -+ /* Wait for the next buffer to be free */ -+ while (bh->state != BUF_STATE_EMPTY) { -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ -+ nsend = min(fsg->usb_amount_left, (u32) mod_data.buflen); -+ memset(bh->buf + nkeep, 0, nsend - nkeep); -+ bh->inreq->length = nsend; -+ bh->inreq->zero = 0; -+ start_transfer(fsg, fsg->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ bh = fsg->next_buffhd_to_fill = bh->next; -+ fsg->usb_amount_left -= nsend; -+ nkeep = 0; -+ } -+ return 0; -+} -+ -+static int throw_away_data(struct fsg_dev *fsg) -+{ -+ struct fsg_buffhd *bh; -+ u32 amount; -+ int rc; -+ -+ while ((bh = fsg->next_buffhd_to_drain)->state != BUF_STATE_EMPTY || -+ fsg->usb_amount_left > 0) { -+ -+ /* Throw away the data in a filled buffer */ -+ if (bh->state == BUF_STATE_FULL) { -+ bh->state = BUF_STATE_EMPTY; -+ fsg->next_buffhd_to_drain = bh->next; -+ -+ /* A short packet or an error ends everything */ -+ if (bh->outreq->actual != bh->outreq->length || -+ bh->outreq->status != 0) { -+ raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); -+ return -EINTR; -+ } -+ continue; -+ } -+ -+ /* Try to submit another request if we need one */ -+ bh = fsg->next_buffhd_to_fill; -+ if (bh->state == BUF_STATE_EMPTY && fsg->usb_amount_left > 0) { -+ amount = min(fsg->usb_amount_left, -+ (u32) mod_data.buflen); -+ -+ /* amount is always divisible by 512, hence by -+ * the bulk-out maxpacket size */ -+ bh->outreq->length = bh->bulk_out_intended_length = -+ amount; -+ start_transfer(fsg, fsg->bulk_out, bh->outreq, -+ &bh->outreq_busy, &bh->state); -+ fsg->next_buffhd_to_fill = bh->next; -+ fsg->usb_amount_left -= amount; -+ continue; -+ } -+ -+ /* Otherwise wait for something to happen */ -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ return 0; -+} -+ -+ -+static int finish_reply(struct fsg_dev *fsg) -+{ -+ struct fsg_buffhd *bh = fsg->next_buffhd_to_fill; -+ int rc = 0; -+ -+ switch (fsg->data_dir) { -+ case DATA_DIR_NONE: -+ break; // Nothing to send -+ -+ /* If we don't know whether the host wants to read or write, -+ * this must be CB or CBI with an unknown command. We mustn't -+ * try to send or receive any data. So stall both bulk pipes -+ * if we can and wait for a reset. */ -+ case DATA_DIR_UNKNOWN: -+ if (mod_data.can_stall) { -+ fsg_set_halt(fsg, fsg->bulk_out); -+ rc = halt_bulk_in_endpoint(fsg); -+ } -+ break; -+ -+ /* All but the last buffer of data must have already been sent */ -+ case DATA_DIR_TO_HOST: -+ if (fsg->data_size == 0) -+ ; // Nothing to send -+ -+ /* If there's no residue, simply send the last buffer */ -+ else if (fsg->residue == 0) { -+ bh->inreq->zero = 0; -+ start_transfer(fsg, fsg->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ fsg->next_buffhd_to_fill = bh->next; -+ } -+ -+ /* There is a residue. For CB and CBI, simply mark the end -+ * of the data with a short packet. However, if we are -+ * allowed to stall, there was no data at all (residue == -+ * data_size), and the command failed (invalid LUN or -+ * sense data is set), then halt the bulk-in endpoint -+ * instead. */ -+ else if (!transport_is_bbb()) { -+ if (mod_data.can_stall && -+ fsg->residue == fsg->data_size && -+ (!fsg->curlun || fsg->curlun->sense_data != SS_NO_SENSE)) { -+ bh->state = BUF_STATE_EMPTY; -+ rc = halt_bulk_in_endpoint(fsg); -+ } else { -+ bh->inreq->zero = 1; -+ start_transfer(fsg, fsg->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ fsg->next_buffhd_to_fill = bh->next; -+ } -+ } -+ -+ /* For Bulk-only, if we're allowed to stall then send the -+ * short packet and halt the bulk-in endpoint. If we can't -+ * stall, pad out the remaining data with 0's. */ -+ else { -+ if (mod_data.can_stall) { -+ bh->inreq->zero = 1; -+ start_transfer(fsg, fsg->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ fsg->next_buffhd_to_fill = bh->next; -+ rc = halt_bulk_in_endpoint(fsg); -+ } else -+ rc = pad_with_zeros(fsg); -+ } -+ break; -+ -+ /* We have processed all we want from the data the host has sent. -+ * There may still be outstanding bulk-out requests. */ -+ case DATA_DIR_FROM_HOST: -+ if (fsg->residue == 0) -+ ; // Nothing to receive -+ -+ /* Did the host stop sending unexpectedly early? */ -+ else if (fsg->short_packet_received) { -+ raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); -+ rc = -EINTR; -+ } -+ -+ /* We haven't processed all the incoming data. If we are -+ * allowed to stall, halt the bulk-out endpoint and cancel -+ * any outstanding requests. */ -+ else if (mod_data.can_stall) { -+ fsg_set_halt(fsg, fsg->bulk_out); -+ raise_exception(fsg, FSG_STATE_ABORT_BULK_OUT); -+ rc = -EINTR; -+ } -+ -+ /* We can't stall. Read in the excess data and throw it -+ * all away. */ -+ else -+ rc = throw_away_data(fsg); -+ break; -+ } -+ return rc; -+} -+ -+ -+static int send_status(struct fsg_dev *fsg) -+{ -+ struct lun *curlun = fsg->curlun; -+ struct fsg_buffhd *bh; -+ int rc; -+ u8 status = USB_STATUS_PASS; -+ u32 sd, sdinfo = 0; -+ -+ /* Wait for the next buffer to become available */ -+ bh = fsg->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ -+ if (curlun) { -+ sd = curlun->sense_data; -+ sdinfo = curlun->sense_data_info; -+ } else if (fsg->bad_lun_okay) -+ sd = SS_NO_SENSE; -+ else -+ sd = SS_LOGICAL_UNIT_NOT_SUPPORTED; -+ -+ if (fsg->phase_error) { -+ DBG(fsg, "sending phase-error status\n"); -+ status = USB_STATUS_PHASE_ERROR; -+ sd = SS_INVALID_COMMAND; -+ } else if (sd != SS_NO_SENSE) { -+ DBG(fsg, "sending command-failure status\n"); -+ status = USB_STATUS_FAIL; -+ VDBG(fsg, " sense data: SK x%02x, ASC x%02x, ASCQ x%02x;" -+ " info x%x\n", -+ SK(sd), ASC(sd), ASCQ(sd), sdinfo); -+ } -+ -+ if (transport_is_bbb()) { -+ struct bulk_cs_wrap *csw = (struct bulk_cs_wrap *) bh->buf; -+ -+ /* Store and send the Bulk-only CSW */ -+ csw->Signature = __constant_cpu_to_le32(USB_BULK_CS_SIG); -+ csw->Tag = fsg->tag; -+ csw->Residue = cpu_to_le32(fsg->residue); -+ csw->Status = status; -+ -+ bh->inreq->length = USB_BULK_CS_WRAP_LEN; -+ bh->inreq->zero = 0; -+ start_transfer(fsg, fsg->bulk_in, bh->inreq, -+ &bh->inreq_busy, &bh->state); -+ -+ } else if (mod_data.transport_type == USB_PR_CB) { -+ -+ /* Control-Bulk transport has no status stage! */ -+ return 0; -+ -+ } else { // USB_PR_CBI -+ struct interrupt_data *buf = (struct interrupt_data *) -+ bh->buf; -+ -+ /* Store and send the Interrupt data. UFI sends the ASC -+ * and ASCQ bytes. Everything else sends a Type (which -+ * is always 0) and the status Value. */ -+ if (mod_data.protocol_type == USB_SC_UFI) { -+ buf->bType = ASC(sd); -+ buf->bValue = ASCQ(sd); -+ } else { -+ buf->bType = 0; -+ buf->bValue = status; -+ } -+ fsg->intreq->length = CBI_INTERRUPT_DATA_LEN; -+ -+ fsg->intr_buffhd = bh; // Point to the right buffhd -+ fsg->intreq->buf = bh->inreq->buf; -+ fsg->intreq->dma = bh->inreq->dma; -+ fsg->intreq->context = bh; -+ start_transfer(fsg, fsg->intr_in, fsg->intreq, -+ &fsg->intreq_busy, &bh->state); -+ } -+ -+ fsg->next_buffhd_to_fill = bh->next; -+ return 0; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Check whether the command is properly formed and whether its data size -+ * and direction agree with the values we already have. */ -+static int check_command(struct fsg_dev *fsg, int cmnd_size, -+ enum data_direction data_dir, unsigned int mask, -+ int needs_medium, const char *name) -+{ -+ int i; -+ int lun = fsg->cmnd[1] >> 5; -+ static const char dirletter[4] = {'u', 'o', 'i', 'n'}; -+ char hdlen[20]; -+ struct lun *curlun; -+ -+ /* Adjust the expected cmnd_size for protocol encapsulation padding. -+ * Transparent SCSI doesn't pad. */ -+ if (protocol_is_scsi()) -+ ; -+ -+ /* There's some disagreement as to whether RBC pads commands or not. -+ * We'll play it safe and accept either form. */ -+ else if (mod_data.protocol_type == USB_SC_RBC) { -+ if (fsg->cmnd_size == 12) -+ cmnd_size = 12; -+ -+ /* All the other protocols pad to 12 bytes */ -+ } else -+ cmnd_size = 12; -+ -+ hdlen[0] = 0; -+ if (fsg->data_dir != DATA_DIR_UNKNOWN) -+ sprintf(hdlen, ", H%c=%u", dirletter[(int) fsg->data_dir], -+ fsg->data_size); -+ VDBG(fsg, "SCSI command: %s; Dc=%d, D%c=%u; Hc=%d%s\n", -+ name, cmnd_size, dirletter[(int) data_dir], -+ fsg->data_size_from_cmnd, fsg->cmnd_size, hdlen); -+ -+ /* We can't reply at all until we know the correct data direction -+ * and size. */ -+ if (fsg->data_size_from_cmnd == 0) -+ data_dir = DATA_DIR_NONE; -+ if (fsg->data_dir == DATA_DIR_UNKNOWN) { // CB or CBI -+ fsg->data_dir = data_dir; -+ fsg->data_size = fsg->data_size_from_cmnd; -+ -+ } else { // Bulk-only -+ if (fsg->data_size < fsg->data_size_from_cmnd) { -+ -+ /* Host data size < Device data size is a phase error. -+ * Carry out the command, but only transfer as much -+ * as we are allowed. */ -+ fsg->data_size_from_cmnd = fsg->data_size; -+ fsg->phase_error = 1; -+ } -+ } -+ fsg->residue = fsg->usb_amount_left = fsg->data_size; -+ -+ /* Conflicting data directions is a phase error */ -+ if (fsg->data_dir != data_dir && fsg->data_size_from_cmnd > 0) -+ goto phase_error; -+ -+ /* Verify the length of the command itself */ -+ if (cmnd_size != fsg->cmnd_size) { -+ -+ /* Special case workaround: MS-Windows issues REQUEST SENSE -+ * with cbw->Length == 12 (it should be 6). */ -+ if (fsg->cmnd[0] == SC_REQUEST_SENSE && fsg->cmnd_size == 12) -+ cmnd_size = fsg->cmnd_size; -+ else -+ goto phase_error; -+ } -+ -+ /* Check that the LUN values are oonsistent */ -+ if (transport_is_bbb()) { -+ if (fsg->lun != lun) -+ DBG(fsg, "using LUN %d from CBW, " -+ "not LUN %d from CDB\n", -+ fsg->lun, lun); -+ } else -+ fsg->lun = lun; // Use LUN from the command -+ -+ /* Check the LUN */ -+ if (fsg->lun >= 0 && fsg->lun < fsg->nluns) { -+ fsg->curlun = curlun = &fsg->luns[fsg->lun]; -+ if (fsg->cmnd[0] != SC_REQUEST_SENSE) { -+ curlun->sense_data = SS_NO_SENSE; -+ curlun->sense_data_info = 0; -+ } -+ } else { -+ fsg->curlun = curlun = NULL; -+ fsg->bad_lun_okay = 0; -+ -+ /* INQUIRY and REQUEST SENSE commands are explicitly allowed -+ * to use unsupported LUNs; all others may not. */ -+ if (fsg->cmnd[0] != SC_INQUIRY && -+ fsg->cmnd[0] != SC_REQUEST_SENSE) { -+ DBG(fsg, "unsupported LUN %d\n", fsg->lun); -+ return -EINVAL; -+ } -+ } -+ -+ /* If a unit attention condition exists, only INQUIRY and -+ * REQUEST SENSE commands are allowed; anything else must fail. */ -+ if (curlun && curlun->unit_attention_data != SS_NO_SENSE && -+ fsg->cmnd[0] != SC_INQUIRY && -+ fsg->cmnd[0] != SC_REQUEST_SENSE) { -+ curlun->sense_data = curlun->unit_attention_data; -+ curlun->unit_attention_data = SS_NO_SENSE; -+ return -EINVAL; -+ } -+ -+ /* Check that only command bytes listed in the mask are non-zero */ -+ fsg->cmnd[1] &= 0x1f; // Mask away the LUN -+ for (i = 1; i < cmnd_size; ++i) { -+ if (fsg->cmnd[i] && !(mask & (1 << i))) { -+ if (curlun) -+ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; -+ return -EINVAL; -+ } -+ } -+ -+ /* If the medium isn't mounted and the command needs to access -+ * it, return an error. */ -+ if (curlun && !backing_file_is_open(curlun) && needs_medium) { -+ curlun->sense_data = SS_MEDIUM_NOT_PRESENT; -+ return -EINVAL; -+ } -+ -+ return 0; -+ -+phase_error: -+ fsg->phase_error = 1; -+ return -EINVAL; -+} -+ -+ -+static int do_scsi_command(struct fsg_dev *fsg) -+{ -+ struct fsg_buffhd *bh; -+ int rc; -+ int reply = -EINVAL; -+ int i; -+ static char unknown[16]; -+ -+ dump_cdb(fsg); -+ -+ /* Wait for the next buffer to become available for data or status */ -+ bh = fsg->next_buffhd_to_drain = fsg->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ fsg->phase_error = 0; -+ fsg->short_packet_received = 0; -+ -+ down_read(&fsg->filesem); // We're using the backing file -+ switch (fsg->cmnd[0]) { -+ -+ case SC_INQUIRY: -+ fsg->data_size_from_cmnd = fsg->cmnd[4]; -+ if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, -+ (1<<4), 0, -+ "INQUIRY")) == 0) -+ reply = do_inquiry(fsg, bh); -+ break; -+ -+ case SC_MODE_SELECT_6: -+ fsg->data_size_from_cmnd = fsg->cmnd[4]; -+ if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, -+ (1<<1) | (1<<4), 0, -+ "MODE SELECT(6)")) == 0) -+ reply = do_mode_select(fsg, bh); -+ break; -+ -+ case SC_MODE_SELECT_10: -+ fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); -+ if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, -+ (1<<1) | (3<<7), 0, -+ "MODE SELECT(10)")) == 0) -+ reply = do_mode_select(fsg, bh); -+ break; -+ -+ case SC_MODE_SENSE_6: -+ fsg->data_size_from_cmnd = fsg->cmnd[4]; -+ if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, -+ (1<<1) | (1<<2) | (1<<4), 0, -+ "MODE SENSE(6)")) == 0) -+ reply = do_mode_sense(fsg, bh); -+ break; -+ -+ case SC_MODE_SENSE_10: -+ fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); -+ if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, -+ (1<<1) | (1<<2) | (3<<7), 0, -+ "MODE SENSE(10)")) == 0) -+ reply = do_mode_sense(fsg, bh); -+ break; -+ -+ case SC_PREVENT_ALLOW_MEDIUM_REMOVAL: -+ fsg->data_size_from_cmnd = 0; -+ if ((reply = check_command(fsg, 6, DATA_DIR_NONE, -+ (1<<4), 0, -+ "PREVENT-ALLOW MEDIUM REMOVAL")) == 0) -+ reply = do_prevent_allow(fsg); -+ break; -+ -+ case SC_READ_6: -+ i = fsg->cmnd[4]; -+ fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; -+ if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, -+ (7<<1) | (1<<4), 1, -+ "READ(6)")) == 0) -+ reply = do_read(fsg); -+ break; -+ -+ case SC_READ_10: -+ fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; -+ if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, -+ (1<<1) | (0xf<<2) | (3<<7), 1, -+ "READ(10)")) == 0) -+ reply = do_read(fsg); -+ break; -+ -+ case SC_READ_12: -+ fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9; -+ if ((reply = check_command(fsg, 12, DATA_DIR_TO_HOST, -+ (1<<1) | (0xf<<2) | (0xf<<6), 1, -+ "READ(12)")) == 0) -+ reply = do_read(fsg); -+ break; -+ -+ case SC_READ_CAPACITY: -+ fsg->data_size_from_cmnd = 8; -+ if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, -+ (0xf<<2) | (1<<8), 1, -+ "READ CAPACITY")) == 0) -+ reply = do_read_capacity(fsg, bh); -+ break; -+ -+ case SC_READ_FORMAT_CAPACITIES: -+ fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]); -+ if ((reply = check_command(fsg, 10, DATA_DIR_TO_HOST, -+ (3<<7), 1, -+ "READ FORMAT CAPACITIES")) == 0) -+ reply = do_read_format_capacities(fsg, bh); -+ break; -+ -+ case SC_REQUEST_SENSE: -+ fsg->data_size_from_cmnd = fsg->cmnd[4]; -+ if ((reply = check_command(fsg, 6, DATA_DIR_TO_HOST, -+ (1<<4), 0, -+ "REQUEST SENSE")) == 0) -+ reply = do_request_sense(fsg, bh); -+ break; -+ -+ case SC_START_STOP_UNIT: -+ fsg->data_size_from_cmnd = 0; -+ if ((reply = check_command(fsg, 6, DATA_DIR_NONE, -+ (1<<1) | (1<<4), 0, -+ "START-STOP UNIT")) == 0) -+ reply = do_start_stop(fsg); -+ break; -+ -+ case SC_SYNCHRONIZE_CACHE: -+ fsg->data_size_from_cmnd = 0; -+ if ((reply = check_command(fsg, 10, DATA_DIR_NONE, -+ (0xf<<2) | (3<<7), 1, -+ "SYNCHRONIZE CACHE")) == 0) -+ reply = do_synchronize_cache(fsg); -+ break; -+ -+ case SC_TEST_UNIT_READY: -+ fsg->data_size_from_cmnd = 0; -+ reply = check_command(fsg, 6, DATA_DIR_NONE, -+ 0, 1, -+ "TEST UNIT READY"); -+ break; -+ -+ /* Although optional, this command is used by MS-Windows. We -+ * support a minimal version: BytChk must be 0. */ -+ case SC_VERIFY: -+ fsg->data_size_from_cmnd = 0; -+ if ((reply = check_command(fsg, 10, DATA_DIR_NONE, -+ (1<<1) | (0xf<<2) | (3<<7), 1, -+ "VERIFY")) == 0) -+ reply = do_verify(fsg); -+ break; -+ -+ case SC_WRITE_6: -+ i = fsg->cmnd[4]; -+ fsg->data_size_from_cmnd = (i == 0 ? 256 : i) << 9; -+ if ((reply = check_command(fsg, 6, DATA_DIR_FROM_HOST, -+ (7<<1) | (1<<4), 1, -+ "WRITE(6)")) == 0) -+ reply = do_write(fsg); -+ break; -+ -+ case SC_WRITE_10: -+ fsg->data_size_from_cmnd = get_be16(&fsg->cmnd[7]) << 9; -+ if ((reply = check_command(fsg, 10, DATA_DIR_FROM_HOST, -+ (1<<1) | (0xf<<2) | (3<<7), 1, -+ "WRITE(10)")) == 0) -+ reply = do_write(fsg); -+ break; -+ -+ case SC_WRITE_12: -+ fsg->data_size_from_cmnd = get_be32(&fsg->cmnd[6]) << 9; -+ if ((reply = check_command(fsg, 12, DATA_DIR_FROM_HOST, -+ (1<<1) | (0xf<<2) | (0xf<<6), 1, -+ "WRITE(12)")) == 0) -+ reply = do_write(fsg); -+ break; -+ -+ /* Some mandatory commands that we recognize but don't implement. -+ * They don't mean much in this setting. It's left as an exercise -+ * for anyone interested to implement RESERVE and RELEASE in terms -+ * of Posix locks. */ -+ case SC_FORMAT_UNIT: -+ case SC_RELEASE: -+ case SC_RESERVE: -+ case SC_SEND_DIAGNOSTIC: -+ // Fall through -+ -+ default: -+ fsg->data_size_from_cmnd = 0; -+ sprintf(unknown, "Unknown x%02x", fsg->cmnd[0]); -+ if ((reply = check_command(fsg, fsg->cmnd_size, -+ DATA_DIR_UNKNOWN, 0xff, 0, unknown)) == 0) { -+ fsg->curlun->sense_data = SS_INVALID_COMMAND; -+ reply = -EINVAL; -+ } -+ break; -+ } -+ up_read(&fsg->filesem); -+ -+ if (reply == -EINTR || signal_pending(current)) -+ return -EINTR; -+ -+ /* Set up the single reply buffer for finish_reply() */ -+ if (reply == -EINVAL) -+ reply = 0; // Error reply length -+ if (reply >= 0 && fsg->data_dir == DATA_DIR_TO_HOST) { -+ reply = min((u32) reply, fsg->data_size_from_cmnd); -+ bh->inreq->length = reply; -+ bh->state = BUF_STATE_FULL; -+ fsg->residue -= reply; -+ } // Otherwise it's already set -+ -+ return 0; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int received_cbw(struct fsg_dev *fsg, struct fsg_buffhd *bh) -+{ -+ struct usb_request *req = bh->outreq; -+ struct bulk_cb_wrap *cbw = (struct bulk_cb_wrap *) req->buf; -+ -+ /* Was this a real packet? */ -+ if (req->status) -+ return -EINVAL; -+ -+ /* Is the CBW valid? */ -+ if (req->actual != USB_BULK_CB_WRAP_LEN || -+ cbw->Signature != __constant_cpu_to_le32( -+ USB_BULK_CB_SIG)) { -+ DBG(fsg, "invalid CBW: len %u sig 0x%x\n", -+ req->actual, -+ le32_to_cpu(cbw->Signature)); -+ -+ /* The Bulk-only spec says we MUST stall the bulk pipes! -+ * If we want to avoid stalls, set a flag so that we will -+ * clear the endpoint halts at the next reset. */ -+ if (!mod_data.can_stall) -+ set_bit(CLEAR_BULK_HALTS, &fsg->atomic_bitflags); -+ fsg_set_halt(fsg, fsg->bulk_out); -+ halt_bulk_in_endpoint(fsg); -+ return -EINVAL; -+ } -+ -+ /* Is the CBW meaningful? */ -+ if (cbw->Lun >= MAX_LUNS || cbw->Flags & ~USB_BULK_IN_FLAG || -+ cbw->Length < 6 || cbw->Length > MAX_COMMAND_SIZE) { -+ DBG(fsg, "non-meaningful CBW: lun = %u, flags = 0x%x, " -+ "cmdlen %u\n", -+ cbw->Lun, cbw->Flags, cbw->Length); -+ -+ /* We can do anything we want here, so let's stall the -+ * bulk pipes if we are allowed to. */ -+ if (mod_data.can_stall) { -+ fsg_set_halt(fsg, fsg->bulk_out); -+ halt_bulk_in_endpoint(fsg); -+ } -+ return -EINVAL; -+ } -+ -+ /* Save the command for later */ -+ fsg->cmnd_size = cbw->Length; -+ memcpy(fsg->cmnd, cbw->CDB, fsg->cmnd_size); -+ if (cbw->Flags & USB_BULK_IN_FLAG) -+ fsg->data_dir = DATA_DIR_TO_HOST; -+ else -+ fsg->data_dir = DATA_DIR_FROM_HOST; -+ fsg->data_size = le32_to_cpu(cbw->DataTransferLength); -+ if (fsg->data_size == 0) -+ fsg->data_dir = DATA_DIR_NONE; -+ fsg->lun = cbw->Lun; -+ fsg->tag = cbw->Tag; -+ return 0; -+} -+ -+ -+static int get_next_command(struct fsg_dev *fsg) -+{ -+ struct fsg_buffhd *bh; -+ int rc = 0; -+ -+ if (transport_is_bbb()) { -+ -+ /* Wait for the next buffer to become available */ -+ bh = fsg->next_buffhd_to_fill; -+ while (bh->state != BUF_STATE_EMPTY) { -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ -+ /* Queue a request to read a Bulk-only CBW */ -+ set_bulk_out_req_length(fsg, bh, USB_BULK_CB_WRAP_LEN); -+ start_transfer(fsg, fsg->bulk_out, bh->outreq, -+ &bh->outreq_busy, &bh->state); -+ -+ /* We will drain the buffer in software, which means we -+ * can reuse it for the next filling. No need to advance -+ * next_buffhd_to_fill. */ -+ -+ /* Wait for the CBW to arrive */ -+ while (bh->state != BUF_STATE_FULL) { -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ rc = received_cbw(fsg, bh); -+ bh->state = BUF_STATE_EMPTY; -+ -+ } else { // USB_PR_CB or USB_PR_CBI -+ -+ /* Wait for the next command to arrive */ -+ while (fsg->cbbuf_cmnd_size == 0) { -+ if ((rc = sleep_thread(fsg)) != 0) -+ return rc; -+ } -+ -+ /* Is the previous status interrupt request still busy? -+ * The host is allowed to skip reading the status, -+ * so we must cancel it. */ -+ if (fsg->intreq_busy) -+ usb_ep_dequeue(fsg->intr_in, fsg->intreq); -+ -+ /* Copy the command and mark the buffer empty */ -+ fsg->data_dir = DATA_DIR_UNKNOWN; -+ spin_lock_irq(&fsg->lock); -+ fsg->cmnd_size = fsg->cbbuf_cmnd_size; -+ memcpy(fsg->cmnd, fsg->cbbuf_cmnd, fsg->cmnd_size); -+ fsg->cbbuf_cmnd_size = 0; -+ spin_unlock_irq(&fsg->lock); -+ } -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int enable_endpoint(struct fsg_dev *fsg, struct usb_ep *ep, -+ const struct usb_endpoint_descriptor *d) -+{ -+ int rc; -+ -+ ep->driver_data = fsg; -+ rc = usb_ep_enable(ep, d); -+ if (rc) -+ ERROR(fsg, "can't enable %s, result %d\n", ep->name, rc); -+ return rc; -+} -+ -+static int alloc_request(struct fsg_dev *fsg, struct usb_ep *ep, -+ struct usb_request **preq) -+{ -+ *preq = usb_ep_alloc_request(ep, GFP_ATOMIC); -+ if (*preq) -+ return 0; -+ ERROR(fsg, "can't allocate request for %s\n", ep->name); -+ return -ENOMEM; -+} -+ -+/* -+ * Reset interface setting and re-init endpoint state (toggle etc). -+ * Call with altsetting < 0 to disable the interface. The only other -+ * available altsetting is 0, which enables the interface. -+ */ -+static int do_set_interface(struct fsg_dev *fsg, int altsetting) -+{ -+ int rc = 0; -+ int i; -+ const struct usb_endpoint_descriptor *d; -+ -+ if (fsg->running) -+ DBG(fsg, "reset interface\n"); -+ -+reset: -+ /* Deallocate the requests */ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ struct fsg_buffhd *bh = &fsg->buffhds[i]; -+ -+ if (bh->inreq) { -+ usb_ep_free_request(fsg->bulk_in, bh->inreq); -+ bh->inreq = NULL; -+ } -+ if (bh->outreq) { -+ usb_ep_free_request(fsg->bulk_out, bh->outreq); -+ bh->outreq = NULL; -+ } -+ } -+ if (fsg->intreq) { -+ usb_ep_free_request(fsg->intr_in, fsg->intreq); -+ fsg->intreq = NULL; -+ } -+ -+ /* Disable the endpoints */ -+ if (fsg->bulk_in_enabled) { -+ usb_ep_disable(fsg->bulk_in); -+ fsg->bulk_in_enabled = 0; -+ } -+ if (fsg->bulk_out_enabled) { -+ usb_ep_disable(fsg->bulk_out); -+ fsg->bulk_out_enabled = 0; -+ } -+ if (fsg->intr_in_enabled) { -+ usb_ep_disable(fsg->intr_in); -+ fsg->intr_in_enabled = 0; -+ } -+ -+ fsg->running = 0; -+ if (altsetting < 0 || rc != 0) -+ return rc; -+ -+ DBG(fsg, "set interface %d\n", altsetting); -+ -+ /* Enable the endpoints */ -+ d = ep_desc(fsg->gadget, &fs_bulk_in_desc, &hs_bulk_in_desc); -+ if ((rc = enable_endpoint(fsg, fsg->bulk_in, d)) != 0) -+ goto reset; -+ fsg->bulk_in_enabled = 1; -+ -+ d = ep_desc(fsg->gadget, &fs_bulk_out_desc, &hs_bulk_out_desc); -+ if ((rc = enable_endpoint(fsg, fsg->bulk_out, d)) != 0) -+ goto reset; -+ fsg->bulk_out_enabled = 1; -+ fsg->bulk_out_maxpacket = d->wMaxPacketSize; -+ -+ if (transport_is_cbi()) { -+ d = ep_desc(fsg->gadget, &fs_intr_in_desc, &hs_intr_in_desc); -+ if ((rc = enable_endpoint(fsg, fsg->intr_in, d)) != 0) -+ goto reset; -+ fsg->intr_in_enabled = 1; -+ } -+ -+ /* Allocate the requests */ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ struct fsg_buffhd *bh = &fsg->buffhds[i]; -+ -+ if ((rc = alloc_request(fsg, fsg->bulk_in, &bh->inreq)) != 0) -+ goto reset; -+ if ((rc = alloc_request(fsg, fsg->bulk_out, &bh->outreq)) != 0) -+ goto reset; -+ bh->inreq->buf = bh->outreq->buf = bh->buf; -+ bh->inreq->dma = bh->outreq->dma = bh->dma; -+ bh->inreq->context = bh->outreq->context = bh; -+ bh->inreq->complete = bulk_in_complete; -+ bh->outreq->complete = bulk_out_complete; -+ } -+ if (transport_is_cbi()) { -+ if ((rc = alloc_request(fsg, fsg->intr_in, &fsg->intreq)) != 0) -+ goto reset; -+ fsg->intreq->complete = intr_in_complete; -+ } -+ -+ fsg->running = 1; -+ for (i = 0; i < fsg->nluns; ++i) -+ fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; -+ return rc; -+} -+ -+ -+/* -+ * Change our operational configuration. This code must agree with the code -+ * that returns config descriptors, and with interface altsetting code. -+ * -+ * It's also responsible for power management interactions. Some -+ * configurations might not work with our current power sources. -+ * For now we just assume the gadget is always self-powered. -+ */ -+static int do_set_config(struct fsg_dev *fsg, u8 new_config) -+{ -+ int rc = 0; -+ -+ /* Disable the single interface */ -+ if (fsg->config != 0) { -+ DBG(fsg, "reset config\n"); -+ fsg->config = 0; -+ rc = do_set_interface(fsg, -1); -+ } -+ -+ /* Enable the interface */ -+ if (new_config != 0) { -+ fsg->config = new_config; -+ if ((rc = do_set_interface(fsg, 0)) != 0) -+ fsg->config = 0; // Reset on errors -+ else { -+ char *speed; -+ -+ switch (fsg->gadget->speed) { -+ case USB_SPEED_LOW: speed = "low"; break; -+ case USB_SPEED_FULL: speed = "full"; break; -+ case USB_SPEED_HIGH: speed = "high"; break; -+ default: speed = "?"; break; -+ } -+ INFO(fsg, "%s speed config #%d\n", speed, fsg->config); -+ } -+ } -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void handle_exception(struct fsg_dev *fsg) -+{ -+ siginfo_t info; -+ int sig; -+ int i; -+ int num_active; -+ struct fsg_buffhd *bh; -+ enum fsg_state old_state; -+ u8 new_config; -+ struct lun *curlun; -+ unsigned int exception_req_tag; -+ int rc; -+ -+ /* Clear the existing signals. Anything but SIGUSR1 is converted -+ * into a high-priority EXIT exception. */ -+ for (;;) { -+ spin_lock_irq(¤t->sigmask_lock); -+ sig = dequeue_signal(&fsg->thread_signal_mask, &info); -+ spin_unlock_irq(¤t->sigmask_lock); -+ if (!sig) -+ break; -+ if (sig != SIGUSR1) { -+ if (fsg->state < FSG_STATE_EXIT) -+ DBG(fsg, "Main thread exiting on signal\n"); -+ raise_exception(fsg, FSG_STATE_EXIT); -+ } -+ } -+ -+ /* Cancel all the pending transfers */ -+ if (fsg->intreq_busy) -+ usb_ep_dequeue(fsg->intr_in, fsg->intreq); -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ bh = &fsg->buffhds[i]; -+ if (bh->inreq_busy) -+ usb_ep_dequeue(fsg->bulk_in, bh->inreq); -+ if (bh->outreq_busy) -+ usb_ep_dequeue(fsg->bulk_out, bh->outreq); -+ } -+ -+ /* Wait until everything is idle */ -+ for (;;) { -+ num_active = fsg->intreq_busy; -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ bh = &fsg->buffhds[i]; -+ num_active += bh->inreq_busy + bh->outreq_busy; -+ } -+ if (num_active == 0) -+ break; -+ if (sleep_thread(fsg)) -+ return; -+ } -+ -+ /* Clear out the controller's fifos */ -+ if (fsg->bulk_in_enabled) -+ usb_ep_fifo_flush(fsg->bulk_in); -+ if (fsg->bulk_out_enabled) -+ usb_ep_fifo_flush(fsg->bulk_out); -+ if (fsg->intr_in_enabled) -+ usb_ep_fifo_flush(fsg->intr_in); -+ -+ /* Reset the I/O buffer states and pointers, the SCSI -+ * state, and the exception. Then invoke the handler. */ -+ spin_lock_irq(&fsg->lock); -+ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ bh = &fsg->buffhds[i]; -+ bh->state = BUF_STATE_EMPTY; -+ } -+ fsg->next_buffhd_to_fill = fsg->next_buffhd_to_drain = -+ &fsg->buffhds[0]; -+ -+ exception_req_tag = fsg->exception_req_tag; -+ new_config = fsg->new_config; -+ old_state = fsg->state; -+ -+ if (old_state == FSG_STATE_ABORT_BULK_OUT) -+ fsg->state = FSG_STATE_STATUS_PHASE; -+ else { -+ for (i = 0; i < fsg->nluns; ++i) { -+ curlun = &fsg->luns[i]; -+ curlun->prevent_medium_removal = 0; -+ curlun->sense_data = curlun->unit_attention_data = -+ SS_NO_SENSE; -+ curlun->sense_data_info = 0; -+ } -+ fsg->state = FSG_STATE_IDLE; -+ } -+ spin_unlock_irq(&fsg->lock); -+ -+ /* Carry out any extra actions required for the exception */ -+ switch (old_state) { -+ default: -+ break; -+ -+ case FSG_STATE_ABORT_BULK_OUT: -+ send_status(fsg); -+ spin_lock_irq(&fsg->lock); -+ if (fsg->state == FSG_STATE_STATUS_PHASE) -+ fsg->state = FSG_STATE_IDLE; -+ spin_unlock_irq(&fsg->lock); -+ break; -+ -+ case FSG_STATE_RESET: -+ /* In case we were forced against our will to halt a -+ * bulk endpoint, clear the halt now. (The SuperH UDC -+ * requires this.) */ -+ if (test_and_clear_bit(CLEAR_BULK_HALTS, -+ &fsg->atomic_bitflags)) { -+ usb_ep_clear_halt(fsg->bulk_in); -+ usb_ep_clear_halt(fsg->bulk_out); -+ } -+ -+ if (transport_is_bbb()) { -+ if (fsg->ep0_req_tag == exception_req_tag) -+ ep0_queue(fsg); // Complete the status stage -+ -+ } else if (transport_is_cbi()) -+ send_status(fsg); // Status by interrupt pipe -+ -+ /* Technically this should go here, but it would only be -+ * a waste of time. Ditto for the INTERFACE_CHANGE and -+ * CONFIG_CHANGE cases. */ -+ // for (i = 0; i < fsg->nluns; ++i) -+ // fsg->luns[i].unit_attention_data = SS_RESET_OCCURRED; -+ break; -+ -+ case FSG_STATE_INTERFACE_CHANGE: -+ rc = do_set_interface(fsg, 0); -+ if (fsg->ep0_req_tag != exception_req_tag) -+ break; -+ if (rc != 0) // STALL on errors -+ fsg_set_halt(fsg, fsg->ep0); -+ else // Complete the status stage -+ ep0_queue(fsg); -+ break; -+ -+ case FSG_STATE_CONFIG_CHANGE: -+ rc = do_set_config(fsg, new_config); -+ if (fsg->ep0_req_tag != exception_req_tag) -+ break; -+ if (rc != 0) // STALL on errors -+ fsg_set_halt(fsg, fsg->ep0); -+ else // Complete the status stage -+ ep0_queue(fsg); -+ break; -+ -+ case FSG_STATE_DISCONNECT: -+ fsync_all(fsg); -+ do_set_config(fsg, 0); // Unconfigured state -+ break; -+ -+ case FSG_STATE_EXIT: -+ case FSG_STATE_TERMINATED: -+ do_set_config(fsg, 0); // Free resources -+ spin_lock_irq(&fsg->lock); -+ fsg->state = FSG_STATE_TERMINATED; // Stop the thread -+ spin_unlock_irq(&fsg->lock); -+ break; -+ } -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int fsg_main_thread(void *fsg_) -+{ -+ struct fsg_dev *fsg = (struct fsg_dev *) fsg_; -+ -+ fsg->thread_task = current; -+ -+ /* Release all our userspace resources */ -+ daemonize(); -+ reparent_to_init(); -+ strncpy(current->comm, "file-storage-gadget", -+ sizeof(current->comm) - 1); -+ -+ /* Allow the thread to be killed by a signal, but set the signal mask -+ * to block everything but INT, TERM, KILL, and USR1. */ -+ siginitsetinv(&fsg->thread_signal_mask, sigmask(SIGINT) | -+ sigmask(SIGTERM) | sigmask(SIGKILL) | -+ sigmask(SIGUSR1)); -+ spin_lock_irq(¤t->sigmask_lock); -+ flush_signals(current); -+ current->blocked = fsg->thread_signal_mask; -+ recalc_sigpending(current); -+ spin_unlock_irq(¤t->sigmask_lock); -+ -+ /* Arrange for userspace references to be interpreted as kernel -+ * pointers. That way we can pass a kernel pointer to a routine -+ * that expects a __user pointer and it will work okay. */ -+ set_fs(get_ds()); -+ -+ /* Wait for the gadget registration to finish up */ -+ wait_for_completion(&fsg->thread_notifier); -+ -+ /* The main loop */ -+ while (fsg->state != FSG_STATE_TERMINATED) { -+ if (exception_in_progress(fsg) || signal_pending(current)) { -+ handle_exception(fsg); -+ continue; -+ } -+ -+ if (!fsg->running) { -+ sleep_thread(fsg); -+ continue; -+ } -+ -+ if (get_next_command(fsg)) -+ continue; -+ -+ spin_lock_irq(&fsg->lock); -+ if (!exception_in_progress(fsg)) -+ fsg->state = FSG_STATE_DATA_PHASE; -+ spin_unlock_irq(&fsg->lock); -+ -+ if (do_scsi_command(fsg) || finish_reply(fsg)) -+ continue; -+ -+ spin_lock_irq(&fsg->lock); -+ if (!exception_in_progress(fsg)) -+ fsg->state = FSG_STATE_STATUS_PHASE; -+ spin_unlock_irq(&fsg->lock); -+ -+ if (send_status(fsg)) -+ continue; -+ -+ spin_lock_irq(&fsg->lock); -+ if (!exception_in_progress(fsg)) -+ fsg->state = FSG_STATE_IDLE; -+ spin_unlock_irq(&fsg->lock); -+ } -+ -+ fsg->thread_task = NULL; -+ flush_signals(current); -+ -+ /* In case we are exiting because of a signal, unregister the -+ * gadget driver and close the backing file. */ -+ if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) { -+ usb_gadget_unregister_driver(&fsg_driver); -+ close_all_backing_files(fsg); -+ } -+ -+ /* Let the unbind and cleanup routines know the thread has exited */ -+ complete_and_exit(&fsg->thread_notifier, 0); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* If the next two routines are called while the gadget is registered, -+ * the caller must own fsg->filesem for writing. */ -+ -+static int NORMALLY_INIT open_backing_file(struct lun *curlun, -+ const char *filename) -+{ -+ int ro; -+ struct file *filp = NULL; -+ int rc = -EINVAL; -+ struct inode *inode = NULL; -+ loff_t size; -+ loff_t num_sectors; -+ -+ /* R/W if we can, R/O if we must */ -+ ro = curlun->ro; -+ if (!ro) { -+ filp = filp_open(filename, O_RDWR | O_LARGEFILE, 0); -+ if (-EROFS == PTR_ERR(filp)) -+ ro = 1; -+ } -+ if (ro) -+ filp = filp_open(filename, O_RDONLY | O_LARGEFILE, 0); -+ if (IS_ERR(filp)) { -+ LINFO(curlun, "unable to open backing file: %s\n", filename); -+ return PTR_ERR(filp); -+ } -+ -+ if (!(filp->f_mode & FMODE_WRITE)) -+ ro = 1; -+ -+ if (filp->f_dentry) -+ inode = filp->f_dentry->d_inode; -+ if (inode && S_ISBLK(inode->i_mode)) { -+ kdev_t dev = inode->i_rdev; -+ -+ if (blk_size[MAJOR(dev)]) -+ size = (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << -+ BLOCK_SIZE_BITS; -+ else { -+ LINFO(curlun, "unable to find file size: %s\n", -+ filename); -+ goto out; -+ } -+ } else if (inode && S_ISREG(inode->i_mode)) -+ size = inode->i_size; -+ else { -+ LINFO(curlun, "invalid file type: %s\n", filename); -+ goto out; -+ } -+ -+ /* If we can't read the file, it's no good. -+ * If we can't write the file, use it read-only. */ -+ if (!filp->f_op || !filp->f_op->read) { -+ LINFO(curlun, "file not readable: %s\n", filename); -+ goto out; -+ } -+ if (IS_RDONLY(inode) || !filp->f_op->write) -+ ro = 1; -+ -+ num_sectors = size >> 9; // File size in 512-byte sectors -+ if (num_sectors == 0) { -+ LINFO(curlun, "file too small: %s\n", filename); -+ rc = -ETOOSMALL; -+ goto out; -+ } -+ -+ get_file(filp); -+ curlun->ro = ro; -+ curlun->filp = filp; -+ curlun->file_length = size; -+ curlun->num_sectors = num_sectors; -+ LDBG(curlun, "open backing file: %s\n", filename); -+ rc = 0; -+ -+out: -+ filp_close(filp, current->files); -+ return rc; -+} -+ -+ -+static void close_backing_file(struct lun *curlun) -+{ -+ if (curlun->filp) { -+ LDBG(curlun, "close backing file\n"); -+ fput(curlun->filp); -+ curlun->filp = NULL; -+ } -+} -+ -+static void close_all_backing_files(struct fsg_dev *fsg) -+{ -+ int i; -+ -+ for (i = 0; i < fsg->nluns; ++i) -+ close_backing_file(&fsg->luns[i]); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void fsg_unbind(struct usb_gadget *gadget) -+{ -+ struct fsg_dev *fsg = get_gadget_data(gadget); -+ int i; -+ struct usb_request *req = fsg->ep0req; -+ -+ DBG(fsg, "unbind\n"); -+ clear_bit(REGISTERED, &fsg->atomic_bitflags); -+ -+ /* If the thread isn't already dead, tell it to exit now */ -+ if (fsg->state != FSG_STATE_TERMINATED) { -+ raise_exception(fsg, FSG_STATE_EXIT); -+ wait_for_completion(&fsg->thread_notifier); -+ -+ /* The cleanup routine waits for this completion also */ -+ complete(&fsg->thread_notifier); -+ } -+ -+ /* Free the data buffers */ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ struct fsg_buffhd *bh = &fsg->buffhds[i]; -+ -+ if (bh->buf) -+ usb_ep_free_buffer(fsg->bulk_in, bh->buf, bh->dma, -+ mod_data.buflen); -+ } -+ -+ /* Free the request and buffer for endpoint 0 */ -+ if (req) { -+ if (req->buf) -+ usb_ep_free_buffer(fsg->ep0, req->buf, -+ req->dma, EP0_BUFSIZE); -+ usb_ep_free_request(fsg->ep0, req); -+ } -+ -+ set_gadget_data(gadget, 0); -+} -+ -+ -+static int __init check_parameters(struct fsg_dev *fsg) -+{ -+ int prot; -+ -+ /* Store the default values */ -+ mod_data.transport_type = USB_PR_BULK; -+ mod_data.transport_name = "Bulk-only"; -+ mod_data.protocol_type = USB_SC_SCSI; -+ mod_data.protocol_name = "Transparent SCSI"; -+ -+ if (gadget_is_sh(fsg->gadget)) -+ mod_data.can_stall = 0; -+ -+ if (mod_data.release == 0xffff) { // Parameter wasn't set -+ if (gadget_is_net2280(fsg->gadget)) -+ mod_data.release = __constant_cpu_to_le16(0x0221); -+ else if (gadget_is_dummy(fsg->gadget)) -+ mod_data.release = __constant_cpu_to_le16(0x0222); -+ else if (gadget_is_pxa(fsg->gadget)) -+ mod_data.release = __constant_cpu_to_le16(0x0223); -+ else if (gadget_is_sh(fsg->gadget)) -+ mod_data.release = __constant_cpu_to_le16(0x0224); -+ -+ /* The sa1100 controller is not supported */ -+ -+ else if (gadget_is_goku(fsg->gadget)) -+ mod_data.release = __constant_cpu_to_le16(0x0226); -+ else if (gadget_is_mq11xx(fsg->gadget)) -+ mod_data.release = __constant_cpu_to_le16(0x0227); -+ else if (gadget_is_omap(fsg->gadget)) -+ mod_data.release = __constant_cpu_to_le16(0x0228); -+ else { -+ WARN(fsg, "controller '%s' not recognized\n", -+ fsg->gadget->name); -+ mod_data.release = __constant_cpu_to_le16(0x0299); -+ } -+ } -+ -+ prot = simple_strtol(mod_data.protocol_parm, NULL, 0); -+ -+#ifdef CONFIG_USB_FILE_STORAGE_TEST -+ if (strnicmp(mod_data.transport_parm, "BBB", 10) == 0) { -+ ; // Use default setting -+ } else if (strnicmp(mod_data.transport_parm, "CB", 10) == 0) { -+ mod_data.transport_type = USB_PR_CB; -+ mod_data.transport_name = "Control-Bulk"; -+ } else if (strnicmp(mod_data.transport_parm, "CBI", 10) == 0) { -+ mod_data.transport_type = USB_PR_CBI; -+ mod_data.transport_name = "Control-Bulk-Interrupt"; -+ } else { -+ ERROR(fsg, "invalid transport: %s\n", mod_data.transport_parm); -+ return -EINVAL; -+ } -+ -+ if (strnicmp(mod_data.protocol_parm, "SCSI", 10) == 0 || -+ prot == USB_SC_SCSI) { -+ ; // Use default setting -+ } else if (strnicmp(mod_data.protocol_parm, "RBC", 10) == 0 || -+ prot == USB_SC_RBC) { -+ mod_data.protocol_type = USB_SC_RBC; -+ mod_data.protocol_name = "RBC"; -+ } else if (strnicmp(mod_data.protocol_parm, "8020", 4) == 0 || -+ strnicmp(mod_data.protocol_parm, "ATAPI", 10) == 0 || -+ prot == USB_SC_8020) { -+ mod_data.protocol_type = USB_SC_8020; -+ mod_data.protocol_name = "8020i (ATAPI)"; -+ } else if (strnicmp(mod_data.protocol_parm, "QIC", 3) == 0 || -+ prot == USB_SC_QIC) { -+ mod_data.protocol_type = USB_SC_QIC; -+ mod_data.protocol_name = "QIC-157"; -+ } else if (strnicmp(mod_data.protocol_parm, "UFI", 10) == 0 || -+ prot == USB_SC_UFI) { -+ mod_data.protocol_type = USB_SC_UFI; -+ mod_data.protocol_name = "UFI"; -+ } else if (strnicmp(mod_data.protocol_parm, "8070", 4) == 0 || -+ prot == USB_SC_8070) { -+ mod_data.protocol_type = USB_SC_8070; -+ mod_data.protocol_name = "8070i"; -+ } else { -+ ERROR(fsg, "invalid protocol: %s\n", mod_data.protocol_parm); -+ return -EINVAL; -+ } -+ -+ mod_data.buflen &= PAGE_CACHE_MASK; -+ if (mod_data.buflen <= 0) { -+ ERROR(fsg, "invalid buflen\n"); -+ return -ETOOSMALL; -+ } -+#endif /* CONFIG_USB_FILE_STORAGE_TEST */ -+ -+ return 0; -+} -+ -+ -+static int __init fsg_bind(struct usb_gadget *gadget) -+{ -+ struct fsg_dev *fsg = the_fsg; -+ int rc; -+ int i; -+ struct lun *curlun; -+ struct usb_ep *ep; -+ struct usb_request *req; -+ char *pathbuf, *p; -+ -+ fsg->gadget = gadget; -+ set_gadget_data(gadget, fsg); -+ fsg->ep0 = gadget->ep0; -+ fsg->ep0->driver_data = fsg; -+ -+ if ((rc = check_parameters(fsg)) != 0) -+ goto out; -+ -+ /* Find out how many LUNs there should be */ -+ i = mod_data.nluns; -+ if (i == 0) { -+ for (i = MAX_LUNS; i > 1; --i) { -+ if (file[i - 1]) -+ break; -+ } -+ } -+ if (i > MAX_LUNS) { -+ ERROR(fsg, "invalid number of LUNs: %d\n", i); -+ rc = -EINVAL; -+ goto out; -+ } -+ -+ /* Create the LUNs and open their backing files. We can't register -+ * the LUN devices until the gadget itself is registered, which -+ * doesn't happen until after fsg_bind() returns. */ -+ fsg->luns = kmalloc(i * sizeof(struct lun), GFP_KERNEL); -+ if (!fsg->luns) { -+ rc = -ENOMEM; -+ goto out; -+ } -+ memset(fsg->luns, 0, i * sizeof(struct lun)); -+ fsg->nluns = i; -+ -+ for (i = 0; i < fsg->nluns; ++i) { -+ curlun = &fsg->luns[i]; -+ curlun->ro = ro[i]; -+ curlun->dev.driver_data = fsg; -+ snprintf(curlun->dev.name, BUS_ID_SIZE, -+ "%s-lun%d", gadget->name, i); -+ -+ if (file[i] && *file[i]) { -+ if ((rc = open_backing_file(curlun, file[i])) != 0) -+ goto out; -+ } else if (!mod_data.removable) { -+ ERROR(fsg, "no file given for LUN%d\n", i); -+ rc = -EINVAL; -+ goto out; -+ } -+ } -+ -+ /* Find all the endpoints we will use */ -+ usb_ep_autoconfig_reset(gadget); -+ ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc); -+ if (!ep) -+ goto autoconf_fail; -+ ep->driver_data = fsg; // claim the endpoint -+ fsg->bulk_in = ep; -+ -+ ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc); -+ if (!ep) -+ goto autoconf_fail; -+ ep->driver_data = fsg; // claim the endpoint -+ fsg->bulk_out = ep; -+ -+ if (transport_is_cbi()) { -+ ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc); -+ if (!ep) -+ goto autoconf_fail; -+ ep->driver_data = fsg; // claim the endpoint -+ fsg->intr_in = ep; -+ } -+ -+ /* Fix up the descriptors */ -+ device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket; -+ device_desc.idVendor = cpu_to_le16(mod_data.vendor); -+ device_desc.idProduct = cpu_to_le16(mod_data.product); -+ device_desc.bcdDevice = cpu_to_le16(mod_data.release); -+ -+ i = (transport_is_cbi() ? 3 : 2); // Number of endpoints -+ intf_desc.bNumEndpoints = i; -+ intf_desc.bInterfaceSubClass = mod_data.protocol_type; -+ intf_desc.bInterfaceProtocol = mod_data.transport_type; -+ fs_function[i+1] = NULL; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ hs_function[i+1] = NULL; -+ -+ /* Assume ep0 uses the same maxpacket value for both speeds */ -+ dev_qualifier.bMaxPacketSize0 = fsg->ep0->maxpacket; -+ -+ /* Assume that all endpoint addresses are the same for both speeds */ -+ hs_bulk_in_desc.bEndpointAddress = fs_bulk_in_desc.bEndpointAddress; -+ hs_bulk_out_desc.bEndpointAddress = fs_bulk_out_desc.bEndpointAddress; -+ hs_intr_in_desc.bEndpointAddress = fs_intr_in_desc.bEndpointAddress; -+#endif -+ -+ rc = -ENOMEM; -+ -+ /* Allocate the request and buffer for endpoint 0 */ -+ fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); -+ if (!req) -+ goto out; -+ req->buf = usb_ep_alloc_buffer(fsg->ep0, EP0_BUFSIZE, -+ &req->dma, GFP_KERNEL); -+ if (!req->buf) -+ goto out; -+ req->complete = ep0_complete; -+ -+ /* Allocate the data buffers */ -+ for (i = 0; i < NUM_BUFFERS; ++i) { -+ struct fsg_buffhd *bh = &fsg->buffhds[i]; -+ -+ bh->buf = usb_ep_alloc_buffer(fsg->bulk_in, mod_data.buflen, -+ &bh->dma, GFP_KERNEL); -+ if (!bh->buf) -+ goto out; -+ bh->next = bh + 1; -+ } -+ fsg->buffhds[NUM_BUFFERS - 1].next = &fsg->buffhds[0]; -+ -+ /* This should reflect the actual gadget power source */ -+ usb_gadget_set_selfpowered(gadget); -+ -+ snprintf(manufacturer, sizeof manufacturer, -+ UTS_SYSNAME " " UTS_RELEASE " with %s", -+ gadget->name); -+ -+ /* On a real device, serial[] would be loaded from permanent -+ * storage. We just encode it from the driver version string. */ -+ for (i = 0; i < sizeof(serial) - 2; i += 2) { -+ unsigned char c = DRIVER_VERSION[i / 2]; -+ -+ if (!c) -+ break; -+ sprintf(&serial[i], "%02X", c); -+ } -+ -+ if ((rc = kernel_thread(fsg_main_thread, fsg, (CLONE_VM | CLONE_FS | -+ CLONE_FILES))) < 0) -+ goto out; -+ fsg->thread_pid = rc; -+ -+ INFO(fsg, DRIVER_DESC ", version: " DRIVER_VERSION "\n"); -+ INFO(fsg, "Number of LUNs=%d\n", fsg->nluns); -+ -+ pathbuf = kmalloc(PATH_MAX, GFP_KERNEL); -+ for (i = 0; i < fsg->nluns; ++i) { -+ curlun = &fsg->luns[i]; -+ if (backing_file_is_open(curlun)) { -+ p = NULL; -+ if (pathbuf) { -+ p = d_path(curlun->filp->f_dentry, -+ curlun->filp->f_vfsmnt, -+ pathbuf, PATH_MAX); -+ if (IS_ERR(p)) -+ p = NULL; -+ } -+ LINFO(curlun, "ro=%d, file: %s\n", -+ curlun->ro, (p ? p : "(error)")); -+ } -+ } -+ kfree(pathbuf); -+ -+ DBG(fsg, "transport=%s (x%02x)\n", -+ mod_data.transport_name, mod_data.transport_type); -+ DBG(fsg, "protocol=%s (x%02x)\n", -+ mod_data.protocol_name, mod_data.protocol_type); -+ DBG(fsg, "VendorID=x%04x, ProductID=x%04x, Release=x%04x\n", -+ mod_data.vendor, mod_data.product, mod_data.release); -+ DBG(fsg, "removable=%d, stall=%d, buflen=%u\n", -+ mod_data.removable, mod_data.can_stall, -+ mod_data.buflen); -+ DBG(fsg, "I/O thread pid: %d\n", fsg->thread_pid); -+ return 0; -+ -+autoconf_fail: -+ ERROR(fsg, "unable to autoconfigure all endpoints\n"); -+ rc = -ENOTSUPP; -+ -+out: -+ fsg->state = FSG_STATE_TERMINATED; // The thread is dead -+ fsg_unbind(gadget); -+ close_all_backing_files(fsg); -+ return rc; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_gadget_driver fsg_driver = { -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ .speed = USB_SPEED_HIGH, -+#else -+ .speed = USB_SPEED_FULL, -+#endif -+ .function = (char *) longname, -+ .bind = fsg_bind, -+ .unbind = fsg_unbind, -+ .disconnect = fsg_disconnect, -+ .setup = fsg_setup, -+ -+ .driver = { -+ .name = (char *) shortname, -+ // .release = ... -+ // .suspend = ... -+ // .resume = ... -+ }, -+}; -+ -+ -+static int __init fsg_alloc(void) -+{ -+ struct fsg_dev *fsg; -+ -+ fsg = kmalloc(sizeof *fsg, GFP_KERNEL); -+ if (!fsg) -+ return -ENOMEM; -+ memset(fsg, 0, sizeof *fsg); -+ spin_lock_init(&fsg->lock); -+ init_rwsem(&fsg->filesem); -+ init_waitqueue_head(&fsg->thread_wqh); -+ init_completion(&fsg->thread_notifier); -+ -+ the_fsg = fsg; -+ return 0; -+} -+ -+ -+static void fsg_free(struct fsg_dev *fsg) -+{ -+ kfree(fsg->luns); -+ kfree(fsg); -+} -+ -+ -+static int __init fsg_init(void) -+{ -+ int rc; -+ struct fsg_dev *fsg; -+ -+ /* Put the module parameters where they belong -- arghh! */ -+ mod_data.nluns = luns; -+ mod_data.transport_parm = transport; -+ mod_data.protocol_parm = protocol; -+ mod_data.removable = removable; -+ mod_data.vendor = vendor; -+ mod_data.product = product; -+ mod_data.release = release; -+ mod_data.buflen = buflen; -+ mod_data.can_stall = stall; -+ -+ if ((rc = fsg_alloc()) != 0) -+ return rc; -+ fsg = the_fsg; -+ if ((rc = usb_gadget_register_driver(&fsg_driver)) != 0) { -+ fsg_free(fsg); -+ return rc; -+ } -+ set_bit(REGISTERED, &fsg->atomic_bitflags); -+ -+ /* Tell the thread to start working */ -+ complete(&fsg->thread_notifier); -+ return 0; -+} -+module_init(fsg_init); -+ -+ -+static void __exit fsg_cleanup(void) -+{ -+ struct fsg_dev *fsg = the_fsg; -+ -+ /* Unregister the driver iff the thread hasn't already done so */ -+ if (test_and_clear_bit(REGISTERED, &fsg->atomic_bitflags)) -+ usb_gadget_unregister_driver(&fsg_driver); -+ -+ /* Wait for the thread to finish up */ -+ wait_for_completion(&fsg->thread_notifier); -+ -+ close_all_backing_files(fsg); -+ fsg_free(fsg); -+} -+module_exit(fsg_cleanup); -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/gadget_chips.h kernel/drivers/usb/gadget/gadget_chips.h ---- /tmp/kernel/drivers/usb/gadget/gadget_chips.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/gadget_chips.h 2005-04-22 17:53:19.434539516 +0200 -@@ -0,0 +1,92 @@ -+/* -+ * USB device controllers have lots of quirks. Use these macros in -+ * gadget drivers or other code that needs to deal with them, and which -+ * autoconfigures instead of using early binding to the hardware. -+ * -+ * This could eventually work like the ARM mach_is_*() stuff, driven by -+ * some config file that gets updated as new hardware is supported. -+ * -+ * NOTE: some of these controller drivers may not be available yet. -+ */ -+#ifdef CONFIG_USB_GADGET_NET2280 -+#define gadget_is_net2280(g) !strcmp("net2280", (g)->name) -+#else -+#define gadget_is_net2280(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_DUMMY_HCD -+#define gadget_is_dummy(g) !strcmp("dummy_udc", (g)->name) -+#else -+#define gadget_is_dummy(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_PXA2XX -+#define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) -+#else -+#define gadget_is_pxa(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_GOKU -+#define gadget_is_goku(g) !strcmp("goku_udc", (g)->name) -+#else -+#define gadget_is_goku(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_SUPERH -+#define gadget_is_sh(g) !strcmp("sh_udc", (g)->name) -+#else -+#define gadget_is_sh(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_SA1100 -+#define gadget_is_sa1100(g) !strcmp("sa1100_udc", (g)->name) -+#else -+#define gadget_is_sa1100(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_LH7A40X -+#define gadget_is_lh7a40x(g) !strcmp("lh7a40x_udc", (g)->name) -+#else -+#define gadget_is_lh7a40x(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_MQ11XX -+#define gadget_is_mq11xx(g) !strcmp("mq11xx_udc", (g)->name) -+#else -+#define gadget_is_mq11xx(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_OMAP -+#define gadget_is_omap(g) !strcmp("omap_udc", (g)->name) -+#else -+#define gadget_is_omap(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_N9604 -+#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name) -+#else -+#define gadget_is_n9604(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_PXA27X -+#define gadget_is_pxa27x(g) !strcmp("pxa27x_udc", (g)->name) -+#else -+#define gadget_is_pxa27x(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_S3C2410 -+#define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name) -+#else -+#define gadget_is_s3c2410(g) 0 -+#endif -+ -+#ifdef CONFIG_USB_GADGET_AT91 -+#define gadget_is_at91(g) !strcmp("at91_udc", (g)->name) -+#else -+#define gadget_is_at91(g) 0 -+#endif -+ -+// CONFIG_USB_GADGET_SX2 -+// CONFIG_USB_GADGET_AU1X00 -+// ... -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/goku_udc.c kernel/drivers/usb/gadget/goku_udc.c ---- /tmp/kernel/drivers/usb/gadget/goku_udc.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/goku_udc.c 2005-04-22 17:53:19.440538539 +0200 -@@ -0,0 +1,1975 @@ -+/* -+ * Toshiba TC86C001 ("Goku-S") USB Device Controller driver -+ * -+ * Copyright (C) 2000-2002 Lineo -+ * by Stuart Lynne, Tom Rushworth, and Bruce Balden -+ * Copyright (C) 2002 Toshiba Corporation -+ * Copyright (C) 2003 MontaVista Software (source@mvista.com) -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+/* -+ * This device has ep0 and three semi-configurable bulk/interrupt endpoints. -+ * -+ * - Endpoint numbering is fixed: ep{1,2,3}-bulk -+ * - Gadget drivers can choose ep maxpacket (8/16/32/64) -+ * - Gadget drivers can choose direction (IN, OUT) -+ * - DMA works with ep1 (OUT transfers) and ep2 (IN transfers). -+ */ -+ -+#undef DEBUG -+// #define VERBOSE /* extra debug messages (success too) */ -+// #define USB_TRACE /* packet-level success messages */ -+ -+#include <linux/config.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/pci.h> -+#include <linux/delay.h> -+#include <linux/ioport.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/smp_lock.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+#include <linux/proc_fs.h> -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include <asm/byteorder.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/unaligned.h> -+ -+ -+#include "goku_udc.h" -+ -+#define DRIVER_DESC "TC86C001 USB Device Controller" -+#define DRIVER_VERSION "30-Oct 2003" -+ -+#define DMA_ADDR_INVALID (~(dma_addr_t)0) -+ -+static const char driver_name [] = "goku_udc"; -+static const char driver_desc [] = DRIVER_DESC; -+ -+MODULE_AUTHOR("source@mvista.com"); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); -+ -+ -+/* -+ * IN dma behaves ok under testing, though the IN-dma abort paths don't -+ * seem to behave quite as expected. Used by default. -+ * -+ * OUT dma documents design problems handling the common "short packet" -+ * transfer termination policy; it couldn't enabled by default, even -+ * if the OUT-dma abort problems had a resolution. -+ */ -+static unsigned use_dma = 1; -+ -+#if 0 -+//#include <linux/moduleparam.h> -+/* "modprobe goku_udc use_dma=1" etc -+ * 0 to disable dma -+ * 1 to use IN dma only (normal operation) -+ * 2 to use IN and OUT dma -+ */ -+module_param(use_dma, uint, S_IRUGO); -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void nuke(struct goku_ep *, int status); -+ -+static inline void -+command(struct goku_udc_regs *regs, int command, unsigned epnum) -+{ -+ writel(COMMAND_EP(epnum) | command, ®s->Command); -+ udelay(300); -+} -+ -+static int -+goku_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) -+{ -+ struct goku_udc *dev; -+ struct goku_ep *ep; -+ u32 mode; -+ u16 max; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct goku_ep, ep); -+ if (!_ep || !desc || ep->desc -+ || desc->bDescriptorType != USB_DT_ENDPOINT) -+ return -EINVAL; -+ dev = ep->dev; -+ if (ep == &dev->ep[0]) -+ return -EINVAL; -+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return -ESHUTDOWN; -+ if (ep->num != (desc->bEndpointAddress & 0x0f)) -+ return -EINVAL; -+ -+ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { -+ case USB_ENDPOINT_XFER_BULK: -+ case USB_ENDPOINT_XFER_INT: -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ if ((readl(ep->reg_status) & EPxSTATUS_EP_MASK) -+ != EPxSTATUS_EP_INVALID) -+ return -EBUSY; -+ -+ /* enabling the no-toggle interrupt mode would need an api hook */ -+ mode = 0; -+ max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); -+ switch (max) { -+ case 64: mode++; -+ case 32: mode++; -+ case 16: mode++; -+ case 8: mode <<= 3; -+ break; -+ default: -+ return -EINVAL; -+ } -+ mode |= 2 << 1; /* bulk, or intr-with-toggle */ -+ -+ /* ep1/ep2 dma direction is chosen early; it works in the other -+ * direction, with pio. be cautious with out-dma. -+ */ -+ ep->is_in = (USB_DIR_IN & desc->bEndpointAddress) != 0; -+ if (ep->is_in) { -+ mode |= 1; -+ ep->dma = (use_dma != 0) && (ep->num == UDC_MSTRD_ENDPOINT); -+ } else { -+ ep->dma = (use_dma == 2) && (ep->num == UDC_MSTWR_ENDPOINT); -+ if (ep->dma) -+ DBG(dev, "%s out-dma hides short packets\n", -+ ep->ep.name); -+ } -+ -+ spin_lock_irqsave(&ep->dev->lock, flags); -+ -+ /* ep1 and ep2 can do double buffering and/or dma */ -+ if (ep->num < 3) { -+ struct goku_udc_regs *regs = ep->dev->regs; -+ u32 tmp; -+ -+ /* double buffer except (for now) with pio in */ -+ tmp = ((ep->dma || !ep->is_in) -+ ? 0x10 /* double buffered */ -+ : 0x11 /* single buffer */ -+ ) << ep->num; -+ tmp |= readl(®s->EPxSingle); -+ writel(tmp, ®s->EPxSingle); -+ -+ tmp = (ep->dma ? 0x10/*dma*/ : 0x11/*pio*/) << ep->num; -+ tmp |= readl(®s->EPxBCS); -+ writel(tmp, ®s->EPxBCS); -+ } -+ writel(mode, ep->reg_mode); -+ command(ep->dev->regs, COMMAND_RESET, ep->num); -+ ep->ep.maxpacket = max; -+ ep->stopped = 0; -+ ep->desc = desc; -+ spin_unlock_irqrestore(&ep->dev->lock, flags); -+ -+ DBG(dev, "enable %s %s %s maxpacket %u\n", ep->ep.name, -+ ep->is_in ? "IN" : "OUT", -+ ep->dma ? "dma" : "pio", -+ max); -+ -+ return 0; -+} -+ -+static void ep_reset(struct goku_udc_regs *regs, struct goku_ep *ep) -+{ -+ struct goku_udc *dev = ep->dev; -+ -+ if (regs) { -+ command(regs, COMMAND_INVALID, ep->num); -+ if (ep->num) { -+ if (ep->num == UDC_MSTWR_ENDPOINT) -+ dev->int_enable &= ~(INT_MSTWREND -+ |INT_MSTWRTMOUT); -+ else if (ep->num == UDC_MSTRD_ENDPOINT) -+ dev->int_enable &= ~INT_MSTRDEND; -+ dev->int_enable &= ~INT_EPxDATASET (ep->num); -+ } else -+ dev->int_enable &= ~INT_EP0; -+ writel(dev->int_enable, ®s->int_enable); -+ readl(®s->int_enable); -+ if (ep->num < 3) { -+ struct goku_udc_regs *regs = ep->dev->regs; -+ u32 tmp; -+ -+ tmp = readl(®s->EPxSingle); -+ tmp &= ~(0x11 << ep->num); -+ writel(tmp, ®s->EPxSingle); -+ -+ tmp = readl(®s->EPxBCS); -+ tmp &= ~(0x11 << ep->num); -+ writel(tmp, ®s->EPxBCS); -+ } -+ /* reset dma in case we're still using it */ -+ if (ep->dma) { -+ u32 master; -+ -+ master = readl(®s->dma_master) & MST_RW_BITS; -+ if (ep->num == UDC_MSTWR_ENDPOINT) { -+ master &= ~MST_W_BITS; -+ master |= MST_WR_RESET; -+ } else { -+ master &= ~MST_R_BITS; -+ master |= MST_RD_RESET; -+ } -+ writel(master, ®s->dma_master); -+ } -+ } -+ -+ ep->ep.maxpacket = MAX_FIFO_SIZE; -+ ep->desc = 0; -+ ep->stopped = 1; -+ ep->irqs = 0; -+ ep->dma = 0; -+} -+ -+static int goku_ep_disable(struct usb_ep *_ep) -+{ -+ struct goku_ep *ep; -+ struct goku_udc *dev; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct goku_ep, ep); -+ if (!_ep || !ep->desc) -+ return -ENODEV; -+ dev = ep->dev; -+ if (dev->ep0state == EP0_SUSPEND) -+ return -EBUSY; -+ -+ VDBG(dev, "disable %s\n", _ep->name); -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ nuke(ep, -ESHUTDOWN); -+ ep_reset(dev->regs, ep); -+ spin_unlock_irqrestore(&dev->lock, flags); -+ -+ return 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_request * -+goku_alloc_request(struct usb_ep *_ep, int gfp_flags) -+{ -+ struct goku_request *req; -+ -+ if (!_ep) -+ return 0; -+ req = kmalloc(sizeof *req, gfp_flags); -+ if (!req) -+ return 0; -+ -+ memset(req, 0, sizeof *req); -+ req->req.dma = DMA_ADDR_INVALID; -+ INIT_LIST_HEAD(&req->queue); -+ return &req->req; -+} -+ -+static void -+goku_free_request(struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct goku_request *req; -+ -+ if (!_ep || !_req) -+ return; -+ -+ req = container_of(_req, struct goku_request, req); -+ WARN_ON(!list_empty(&req->queue)); -+ kfree(req); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+#undef USE_KMALLOC -+ -+/* many common platforms have dma-coherent caches, which means that it's -+ * safe to use kmalloc() memory for all i/o buffers without using any -+ * cache flushing calls. (unless you're trying to share cache lines -+ * between dma and non-dma activities, which is a slow idea in any case.) -+ * -+ * other platforms need more care, with 2.6 having a moderately general -+ * solution except for the common "buffer is smaller than a page" case. -+ */ -+#if defined(CONFIG_X86) -+#define USE_KMALLOC -+ -+#elif defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO) -+#define USE_KMALLOC -+ -+#elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) -+#define USE_KMALLOC -+ -+#endif -+ -+/* allocating buffers this way eliminates dma mapping overhead, which -+ * on some platforms will mean eliminating a per-io buffer copy. with -+ * some kinds of system caches, further tweaks may still be needed. -+ */ -+static void * -+goku_alloc_buffer(struct usb_ep *_ep, unsigned bytes, -+ dma_addr_t *dma, int gfp_flags) -+{ -+ void *retval; -+ struct goku_ep *ep; -+ -+ ep = container_of(_ep, struct goku_ep, ep); -+ if (!_ep) -+ return 0; -+ *dma = DMA_ADDR_INVALID; -+ -+#if defined(USE_KMALLOC) -+ retval = kmalloc(bytes, gfp_flags); -+ if (retval) -+ *dma = virt_to_phys(retval); -+#else -+ if (ep->dma) { -+ /* one problem with this call is that it wastes memory on -+ * typical 1/N page allocations: it allocates 1-N pages. -+ * another is that it always uses GFP_ATOMIC. -+ */ -+#warning Using pci_alloc_consistent even with buffers smaller than a page. -+ retval = pci_alloc_consistent(ep->dev->pdev, bytes, dma); -+ } else -+ retval = kmalloc(bytes, gfp_flags); -+#endif -+ return retval; -+} -+ -+static void -+goku_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) -+{ -+ /* free memory into the right allocator */ -+#ifndef USE_KMALLOC -+ if (dma != DMA_ADDR_INVALID) { -+ struct goku_ep *ep; -+ -+ ep = container_of(_ep, struct goku_ep, ep); -+ if (!_ep) -+ return; -+ /* one problem with this call is that some platforms -+ * don't allow it to be used in_irq(). -+ */ -+ pci_free_consistent(ep->dev->pdev, bytes, buf, dma); -+ } else -+#endif -+ kfree (buf); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void -+done(struct goku_ep *ep, struct goku_request *req, int status) -+{ -+ struct goku_udc *dev; -+ unsigned stopped = ep->stopped; -+ -+ list_del_init(&req->queue); -+ -+ if (likely(req->req.status == -EINPROGRESS)) -+ req->req.status = status; -+ else -+ status = req->req.status; -+ -+ dev = ep->dev; -+ if (req->mapped) { -+ pci_unmap_single(dev->pdev, req->req.dma, req->req.length, -+ ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); -+ req->req.dma = DMA_ADDR_INVALID; -+ req->mapped = 0; -+ } -+ -+#ifndef USB_TRACE -+ if (status && status != -ESHUTDOWN) -+#endif -+ VDBG(dev, "complete %s req %p stat %d len %u/%u\n", -+ ep->ep.name, &req->req, status, -+ req->req.actual, req->req.length); -+ -+ /* don't modify queue heads during completion callback */ -+ ep->stopped = 1; -+ spin_unlock(&dev->lock); -+ req->req.complete(&ep->ep, &req->req); -+ spin_lock(&dev->lock); -+ ep->stopped = stopped; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static inline int -+write_packet(u32 *fifo, u8 *buf, struct goku_request *req, unsigned max) -+{ -+ unsigned length, count; -+ -+ length = min(req->req.length - req->req.actual, max); -+ req->req.actual += length; -+ -+ count = length; -+ while (likely(count--)) -+ writel(*buf++, fifo); -+ return length; -+} -+ -+// return: 0 = still running, 1 = completed, negative = errno -+static int write_fifo(struct goku_ep *ep, struct goku_request *req) -+{ -+ struct goku_udc *dev = ep->dev; -+ u32 tmp; -+ u8 *buf; -+ unsigned count; -+ int is_last; -+ -+ tmp = readl(&dev->regs->DataSet); -+ buf = req->req.buf + req->req.actual; -+ prefetch(buf); -+ -+ dev = ep->dev; -+ if (unlikely(ep->num == 0 && dev->ep0state != EP0_IN)) -+ return -EL2HLT; -+ -+ /* NOTE: just single-buffered PIO-IN for now. */ -+ if (unlikely((tmp & DATASET_A(ep->num)) != 0)) -+ return 0; -+ -+ /* clear our "packet available" irq */ -+ if (ep->num != 0) -+ writel(~INT_EPxDATASET(ep->num), &dev->regs->int_status); -+ -+ count = write_packet(ep->reg_fifo, buf, req, ep->ep.maxpacket); -+ -+ /* last packet often short (sometimes a zlp, especially on ep0) */ -+ if (unlikely(count != ep->ep.maxpacket)) { -+ writel(~(1<<ep->num), &dev->regs->EOP); -+ if (ep->num == 0) { -+ dev->ep[0].stopped = 1; -+ dev->ep0state = EP0_STATUS; -+ } -+ is_last = 1; -+ } else { -+ if (likely(req->req.length != req->req.actual) -+ || req->req.zero) -+ is_last = 0; -+ else -+ is_last = 1; -+ } -+#if 0 /* printk seemed to trash is_last...*/ -+//#ifdef USB_TRACE -+ VDBG(dev, "wrote %s %u bytes%s IN %u left %p\n", -+ ep->ep.name, count, is_last ? "/last" : "", -+ req->req.length - req->req.actual, req); -+#endif -+ -+ /* requests complete when all IN data is in the FIFO, -+ * or sometimes later, if a zlp was needed. -+ */ -+ if (is_last) { -+ done(ep, req, 0); -+ return 1; -+ } -+ -+ return 0; -+} -+ -+static int read_fifo(struct goku_ep *ep, struct goku_request *req) -+{ -+ struct goku_udc_regs *regs; -+ u32 size, set; -+ u8 *buf; -+ unsigned bufferspace, is_short, dbuff; -+ -+ regs = ep->dev->regs; -+top: -+ buf = req->req.buf + req->req.actual; -+ prefetchw(buf); -+ -+ if (unlikely(ep->num == 0 && ep->dev->ep0state != EP0_OUT)) -+ return -EL2HLT; -+ -+ dbuff = (ep->num == 1 || ep->num == 2); -+ do { -+ /* ack dataset irq matching the status we'll handle */ -+ if (ep->num != 0) -+ writel(~INT_EPxDATASET(ep->num), ®s->int_status); -+ -+ set = readl(®s->DataSet) & DATASET_AB(ep->num); -+ size = readl(®s->EPxSizeLA[ep->num]); -+ bufferspace = req->req.length - req->req.actual; -+ -+ /* usually do nothing without an OUT packet */ -+ if (likely(ep->num != 0 || bufferspace != 0)) { -+ if (unlikely(set == 0)) -+ break; -+ /* use ep1/ep2 double-buffering for OUT */ -+ if (!(size & PACKET_ACTIVE)) -+ size = readl(®s->EPxSizeLB[ep->num]); -+ if (!(size & PACKET_ACTIVE)) // "can't happen" -+ break; -+ size &= DATASIZE; /* EPxSizeH == 0 */ -+ -+ /* ep0out no-out-data case for set_config, etc */ -+ } else -+ size = 0; -+ -+ /* read all bytes from this packet */ -+ req->req.actual += size; -+ is_short = (size < ep->ep.maxpacket); -+#ifdef USB_TRACE -+ VDBG(ep->dev, "read %s %u bytes%s OUT req %p %u/%u\n", -+ ep->ep.name, size, is_short ? "/S" : "", -+ req, req->req.actual, req->req.length); -+#endif -+ while (likely(size-- != 0)) { -+ u8 byte = (u8) readl(ep->reg_fifo); -+ -+ if (unlikely(bufferspace == 0)) { -+ /* this happens when the driver's buffer -+ * is smaller than what the host sent. -+ * discard the extra data in this packet. -+ */ -+ if (req->req.status != -EOVERFLOW) -+ DBG(ep->dev, "%s overflow %u\n", -+ ep->ep.name, size); -+ req->req.status = -EOVERFLOW; -+ } else { -+ *buf++ = byte; -+ bufferspace--; -+ } -+ } -+ -+ /* completion */ -+ if (unlikely(is_short || req->req.actual == req->req.length)) { -+ if (unlikely(ep->num == 0)) { -+ /* non-control endpoints now usable? */ -+ if (ep->dev->req_config) -+ writel(ep->dev->configured -+ ? USBSTATE_CONFIGURED -+ : 0, -+ ®s->UsbState); -+ /* ep0out status stage */ -+ writel(~(1<<0), ®s->EOP); -+ ep->stopped = 1; -+ ep->dev->ep0state = EP0_STATUS; -+ } -+ done(ep, req, 0); -+ -+ /* empty the second buffer asap */ -+ if (dbuff && !list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, -+ struct goku_request, queue); -+ goto top; -+ } -+ return 1; -+ } -+ } while (dbuff); -+ return 0; -+} -+ -+static inline void -+pio_irq_enable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum) -+{ -+ dev->int_enable |= INT_EPxDATASET (epnum); -+ writel(dev->int_enable, ®s->int_enable); -+ /* write may still be posted */ -+} -+ -+static inline void -+pio_irq_disable(struct goku_udc *dev, struct goku_udc_regs *regs, int epnum) -+{ -+ dev->int_enable &= ~INT_EPxDATASET (epnum); -+ writel(dev->int_enable, ®s->int_enable); -+ /* write may still be posted */ -+} -+ -+static inline void -+pio_advance(struct goku_ep *ep) -+{ -+ struct goku_request *req; -+ -+ if (unlikely(list_empty (&ep->queue))) -+ return; -+ req = list_entry(ep->queue.next, struct goku_request, queue); -+ (ep->is_in ? write_fifo : read_fifo)(ep, req); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+// return: 0 = q running, 1 = q stopped, negative = errno -+static int start_dma(struct goku_ep *ep, struct goku_request *req) -+{ -+ struct goku_udc_regs *regs = ep->dev->regs; -+ u32 master; -+ u32 start = req->req.dma; -+ u32 end = start + req->req.length - 1; -+ -+ master = readl(®s->dma_master) & MST_RW_BITS; -+ -+ /* re-init the bits affecting IN dma; careful with zlps */ -+ if (likely(ep->is_in)) { -+ if (unlikely(master & MST_RD_ENA)) { -+ DBG (ep->dev, "start, IN active dma %03x!!\n", -+ master); -+// return -EL2HLT; -+ } -+ writel(end, ®s->in_dma_end); -+ writel(start, ®s->in_dma_start); -+ -+ master &= ~MST_R_BITS; -+ if (unlikely(req->req.length == 0)) -+ master = MST_RD_ENA | MST_RD_EOPB; -+ else if ((req->req.length % ep->ep.maxpacket) != 0 -+ || req->req.zero) -+ master = MST_RD_ENA | MST_EOPB_ENA; -+ else -+ master = MST_RD_ENA | MST_EOPB_DIS; -+ -+ ep->dev->int_enable |= INT_MSTRDEND; -+ -+ /* Goku DMA-OUT merges short packets, which plays poorly with -+ * protocols where short packets mark the transfer boundaries. -+ * The chip supports a nonstandard policy with INT_MSTWRTMOUT, -+ * ending transfers after 3 SOFs; we don't turn it on. -+ */ -+ } else { -+ if (unlikely(master & MST_WR_ENA)) { -+ DBG (ep->dev, "start, OUT active dma %03x!!\n", -+ master); -+// return -EL2HLT; -+ } -+ writel(end, ®s->out_dma_end); -+ writel(start, ®s->out_dma_start); -+ -+ master &= ~MST_W_BITS; -+ master |= MST_WR_ENA | MST_TIMEOUT_DIS; -+ -+ ep->dev->int_enable |= INT_MSTWREND|INT_MSTWRTMOUT; -+ } -+ -+ writel(master, ®s->dma_master); -+ writel(ep->dev->int_enable, ®s->int_enable); -+ return 0; -+} -+ -+static void dma_advance(struct goku_udc *dev, struct goku_ep *ep) -+{ -+ struct goku_request *req; -+ struct goku_udc_regs *regs = ep->dev->regs; -+ u32 master; -+ -+ master = readl(®s->dma_master); -+ -+ if (unlikely(list_empty(&ep->queue))) { -+stop: -+ if (ep->is_in) -+ dev->int_enable &= ~INT_MSTRDEND; -+ else -+ dev->int_enable &= ~(INT_MSTWREND|INT_MSTWRTMOUT); -+ writel(dev->int_enable, ®s->int_enable); -+ return; -+ } -+ req = list_entry(ep->queue.next, struct goku_request, queue); -+ -+ /* normal hw dma completion (not abort) */ -+ if (likely(ep->is_in)) { -+ if (unlikely(master & MST_RD_ENA)) -+ return; -+ req->req.actual = readl(®s->in_dma_current); -+ } else { -+ if (unlikely(master & MST_WR_ENA)) -+ return; -+ -+ /* hardware merges short packets, and also hides packet -+ * overruns. a partial packet MAY be in the fifo here. -+ */ -+ req->req.actual = readl(®s->out_dma_current); -+ } -+ req->req.actual -= req->req.dma; -+ req->req.actual++; -+ -+#ifdef USB_TRACE -+ VDBG(dev, "done %s %s dma, %u/%u bytes, req %p\n", -+ ep->ep.name, ep->is_in ? "IN" : "OUT", -+ req->req.actual, req->req.length, req); -+#endif -+ done(ep, req, 0); -+ if (list_empty(&ep->queue)) -+ goto stop; -+ req = list_entry(ep->queue.next, struct goku_request, queue); -+ (void) start_dma(ep, req); -+} -+ -+static void abort_dma(struct goku_ep *ep, int status) -+{ -+ struct goku_udc_regs *regs = ep->dev->regs; -+ struct goku_request *req; -+ u32 curr, master; -+ -+ /* NAK future host requests, hoping the implicit delay lets the -+ * dma engine finish reading (or writing) its latest packet and -+ * empty the dma buffer (up to 16 bytes). -+ * -+ * This avoids needing to clean up a partial packet in the fifo; -+ * we can't do that for IN without side effects to HALT and TOGGLE. -+ */ -+ command(regs, COMMAND_FIFO_DISABLE, ep->num); -+ req = list_entry(ep->queue.next, struct goku_request, queue); -+ master = readl(®s->dma_master) & MST_RW_BITS; -+ -+ /* FIXME using these resets isn't usably documented. this may -+ * not work unless it's followed by disabling the endpoint. -+ * -+ * FIXME the OUT reset path doesn't even behave consistently. -+ */ -+ if (ep->is_in) { -+ if (unlikely((readl(®s->dma_master) & MST_RD_ENA) == 0)) -+ goto finished; -+ curr = readl(®s->in_dma_current); -+ -+ writel(curr, ®s->in_dma_end); -+ writel(curr, ®s->in_dma_start); -+ -+ master &= ~MST_R_BITS; -+ master |= MST_RD_RESET; -+ writel(master, ®s->dma_master); -+ -+ if (readl(®s->dma_master) & MST_RD_ENA) -+ DBG(ep->dev, "IN dma active after reset!\n"); -+ -+ } else { -+ if (unlikely((readl(®s->dma_master) & MST_WR_ENA) == 0)) -+ goto finished; -+ curr = readl(®s->out_dma_current); -+ -+ writel(curr, ®s->out_dma_end); -+ writel(curr, ®s->out_dma_start); -+ -+ master &= ~MST_W_BITS; -+ master |= MST_WR_RESET; -+ writel(master, ®s->dma_master); -+ -+ if (readl(®s->dma_master) & MST_WR_ENA) -+ DBG(ep->dev, "OUT dma active after reset!\n"); -+ } -+ req->req.actual = (curr - req->req.dma) + 1; -+ req->req.status = status; -+ -+ VDBG(ep->dev, "%s %s %s %d/%d\n", __FUNCTION__, ep->ep.name, -+ ep->is_in ? "IN" : "OUT", -+ req->req.actual, req->req.length); -+ -+ command(regs, COMMAND_FIFO_ENABLE, ep->num); -+ -+ return; -+ -+finished: -+ /* dma already completed; no abort needed */ -+ command(regs, COMMAND_FIFO_ENABLE, ep->num); -+ req->req.actual = req->req.length; -+ req->req.status = 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int -+goku_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) -+{ -+ struct goku_request *req; -+ struct goku_ep *ep; -+ struct goku_udc *dev; -+ unsigned long flags; -+ int status; -+ -+ /* always require a cpu-view buffer so pio works */ -+ req = container_of(_req, struct goku_request, req); -+ if (unlikely(!_req || !_req->complete -+ || !_req->buf || !list_empty(&req->queue))) -+ return -EINVAL; -+ ep = container_of(_ep, struct goku_ep, ep); -+ if (unlikely(!_ep || (!ep->desc && ep->num != 0))) -+ return -EINVAL; -+ dev = ep->dev; -+ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) -+ return -ESHUTDOWN; -+ -+ /* can't touch registers when suspended */ -+ if (dev->ep0state == EP0_SUSPEND) -+ return -EBUSY; -+ -+ /* set up dma mapping in case the caller didn't */ -+ if (ep->dma && _req->dma == DMA_ADDR_INVALID) { -+ _req->dma = pci_map_single(dev->pdev, _req->buf, _req->length, -+ ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); -+ req->mapped = 1; -+ } -+ -+#ifdef USB_TRACE -+ VDBG(dev, "%s queue req %p, len %u buf %p\n", -+ _ep->name, _req, _req->length, _req->buf); -+#endif -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ -+ _req->status = -EINPROGRESS; -+ _req->actual = 0; -+ -+ /* for ep0 IN without premature status, zlp is required and -+ * writing EOP starts the status stage (OUT). -+ */ -+ if (unlikely(ep->num == 0 && ep->is_in)) -+ _req->zero = 1; -+ -+ /* kickstart this i/o queue? */ -+ status = 0; -+ if (list_empty(&ep->queue) && likely(!ep->stopped)) { -+ /* dma: done after dma completion IRQ (or error) -+ * pio: done after last fifo operation -+ */ -+ if (ep->dma) -+ status = start_dma(ep, req); -+ else -+ status = (ep->is_in ? write_fifo : read_fifo)(ep, req); -+ -+ if (unlikely(status != 0)) { -+ if (status > 0) -+ status = 0; -+ req = 0; -+ } -+ -+ } /* else pio or dma irq handler advances the queue. */ -+ -+ if (likely(req != 0)) -+ list_add_tail(&req->queue, &ep->queue); -+ -+ if (likely(!list_empty(&ep->queue)) -+ && likely(ep->num != 0) -+ && !ep->dma -+ && !(dev->int_enable & INT_EPxDATASET (ep->num))) -+ pio_irq_enable(dev, dev->regs, ep->num); -+ -+ spin_unlock_irqrestore(&dev->lock, flags); -+ -+ /* pci writes may still be posted */ -+ return status; -+} -+ -+/* dequeue ALL requests */ -+static void nuke(struct goku_ep *ep, int status) -+{ -+ struct goku_request *req; -+ -+ ep->stopped = 1; -+ if (list_empty(&ep->queue)) -+ return; -+ if (ep->dma) -+ abort_dma(ep, status); -+ while (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, struct goku_request, queue); -+ done(ep, req, status); -+ } -+} -+ -+/* dequeue JUST ONE request */ -+static int goku_dequeue(struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct goku_request *req; -+ struct goku_ep *ep; -+ struct goku_udc *dev; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct goku_ep, ep); -+ if (!_ep || !_req || (!ep->desc && ep->num != 0)) -+ return -EINVAL; -+ dev = ep->dev; -+ if (!dev->driver) -+ return -ESHUTDOWN; -+ -+ /* we can't touch (dma) registers when suspended */ -+ if (dev->ep0state == EP0_SUSPEND) -+ return -EBUSY; -+ -+ VDBG(dev, "%s %s %s %s %p\n", __FUNCTION__, _ep->name, -+ ep->is_in ? "IN" : "OUT", -+ ep->dma ? "dma" : "pio", -+ _req); -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ -+ /* make sure it's actually queued on this endpoint */ -+ list_for_each_entry (req, &ep->queue, queue) { -+ if (&req->req == _req) -+ break; -+ } -+ if (&req->req != _req) { -+ spin_unlock_irqrestore (&dev->lock, flags); -+ return -EINVAL; -+ } -+ -+ if (ep->dma && ep->queue.next == &req->queue && !ep->stopped) { -+ abort_dma(ep, -ECONNRESET); -+ done(ep, req, -ECONNRESET); -+ dma_advance(dev, ep); -+ } else if (!list_empty(&req->queue)) -+ done(ep, req, -ECONNRESET); -+ else -+ req = 0; -+ spin_unlock_irqrestore(&dev->lock, flags); -+ -+ return req ? 0 : -EOPNOTSUPP; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void goku_clear_halt(struct goku_ep *ep) -+{ -+ // assert (ep->num !=0) -+ VDBG(ep->dev, "%s clear halt\n", ep->ep.name); -+ command(ep->dev->regs, COMMAND_SETDATA0, ep->num); -+ command(ep->dev->regs, COMMAND_STALL_CLEAR, ep->num); -+ if (ep->stopped) { -+ ep->stopped = 0; -+ if (ep->dma) { -+ struct goku_request *req; -+ -+ if (list_empty(&ep->queue)) -+ return; -+ req = list_entry(ep->queue.next, struct goku_request, -+ queue); -+ (void) start_dma(ep, req); -+ } else -+ pio_advance(ep); -+ } -+} -+ -+static int goku_set_halt(struct usb_ep *_ep, int value) -+{ -+ struct goku_ep *ep; -+ unsigned long flags; -+ int retval = 0; -+ -+ if (!_ep) -+ return -ENODEV; -+ ep = container_of (_ep, struct goku_ep, ep); -+ -+ if (ep->num == 0) { -+ if (value) { -+ ep->dev->ep0state = EP0_STALL; -+ ep->dev->ep[0].stopped = 1; -+ } else -+ return -EINVAL; -+ -+ /* don't change EPxSTATUS_EP_INVALID to READY */ -+ } else if (!ep->desc) { -+ DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name); -+ return -EINVAL; -+ } -+ -+ spin_lock_irqsave(&ep->dev->lock, flags); -+ if (!list_empty(&ep->queue)) -+ retval = -EAGAIN; -+ else if (ep->is_in && value -+ /* data in (either) packet buffer? */ -+ && (ep->dev->regs->DataSet & DATASET_AB(ep->num))) -+ retval = -EAGAIN; -+ else if (!value) -+ goku_clear_halt(ep); -+ else { -+ ep->stopped = 1; -+ VDBG(ep->dev, "%s set halt\n", ep->ep.name); -+ command(ep->dev->regs, COMMAND_STALL, ep->num); -+ readl(ep->reg_status); -+ } -+ spin_unlock_irqrestore(&ep->dev->lock, flags); -+ return retval; -+} -+ -+static int goku_fifo_status(struct usb_ep *_ep) -+{ -+ struct goku_ep *ep; -+ struct goku_udc_regs *regs; -+ u32 size; -+ -+ if (!_ep) -+ return -ENODEV; -+ ep = container_of(_ep, struct goku_ep, ep); -+ -+ /* size is only reported sanely for OUT */ -+ if (ep->is_in) -+ return -EOPNOTSUPP; -+ -+ /* ignores 16-byte dma buffer; SizeH == 0 */ -+ regs = ep->dev->regs; -+ size = readl(®s->EPxSizeLA[ep->num]) & DATASIZE; -+ size += readl(®s->EPxSizeLB[ep->num]) & DATASIZE; -+ VDBG(ep->dev, "%s %s %u\n", __FUNCTION__, ep->ep.name, size); -+ return size; -+} -+ -+static void goku_fifo_flush(struct usb_ep *_ep) -+{ -+ struct goku_ep *ep; -+ struct goku_udc_regs *regs; -+ u32 size; -+ -+ if (!_ep) -+ return; -+ ep = container_of(_ep, struct goku_ep, ep); -+ VDBG(ep->dev, "%s %s\n", __FUNCTION__, ep->ep.name); -+ -+ /* don't change EPxSTATUS_EP_INVALID to READY */ -+ if (!ep->desc && ep->num != 0) { -+ DBG(ep->dev, "%s %s inactive?\n", __FUNCTION__, ep->ep.name); -+ return; -+ } -+ -+ regs = ep->dev->regs; -+ size = readl(®s->EPxSizeLA[ep->num]); -+ size &= DATASIZE; -+ -+ /* Non-desirable behavior: FIFO_CLEAR also clears the -+ * endpoint halt feature. For OUT, we _could_ just read -+ * the bytes out (PIO, if !ep->dma); for in, no choice. -+ */ -+ if (size) -+ command(regs, COMMAND_FIFO_CLEAR, ep->num); -+} -+ -+static struct usb_ep_ops goku_ep_ops = { -+ .enable = goku_ep_enable, -+ .disable = goku_ep_disable, -+ -+ .alloc_request = goku_alloc_request, -+ .free_request = goku_free_request, -+ -+ .alloc_buffer = goku_alloc_buffer, -+ .free_buffer = goku_free_buffer, -+ -+ .queue = goku_queue, -+ .dequeue = goku_dequeue, -+ -+ .set_halt = goku_set_halt, -+ .fifo_status = goku_fifo_status, -+ .fifo_flush = goku_fifo_flush, -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int goku_get_frame(struct usb_gadget *_gadget) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static const struct usb_gadget_ops goku_ops = { -+ .get_frame = goku_get_frame, -+ // no remote wakeup -+ // not selfpowered -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+static inline char *dmastr(void) -+{ -+ if (use_dma == 0) -+ return "(dma disabled)"; -+ else if (use_dma == 2) -+ return "(dma IN and OUT)"; -+ else -+ return "(dma IN)"; -+} -+ -+/* if we're trying to save space, don't bother with this proc file */ -+ -+#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED) -+# define UDC_PROC_FILE -+#endif -+ -+#ifdef UDC_PROC_FILE -+ -+static const char proc_node_name [] = "driver/udc"; -+ -+#define FOURBITS "%s%s%s%s" -+#define EIGHTBITS FOURBITS FOURBITS -+ -+static void -+dump_intmask(const char *label, u32 mask, char **next, unsigned *size) -+{ -+ int t; -+ -+ /* int_status is the same format ... */ -+ t = snprintf(*next, *size, -+ "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n", -+ label, mask, -+ (mask & INT_PWRDETECT) ? " power" : "", -+ (mask & INT_SYSERROR) ? " sys" : "", -+ (mask & INT_MSTRDEND) ? " in-dma" : "", -+ (mask & INT_MSTWRTMOUT) ? " wrtmo" : "", -+ -+ (mask & INT_MSTWREND) ? " out-dma" : "", -+ (mask & INT_MSTWRSET) ? " wrset" : "", -+ (mask & INT_ERR) ? " err" : "", -+ (mask & INT_SOF) ? " sof" : "", -+ -+ (mask & INT_EP3NAK) ? " ep3nak" : "", -+ (mask & INT_EP2NAK) ? " ep2nak" : "", -+ (mask & INT_EP1NAK) ? " ep1nak" : "", -+ (mask & INT_EP3DATASET) ? " ep3" : "", -+ -+ (mask & INT_EP2DATASET) ? " ep2" : "", -+ (mask & INT_EP1DATASET) ? " ep1" : "", -+ (mask & INT_STATUSNAK) ? " ep0snak" : "", -+ (mask & INT_STATUS) ? " ep0status" : "", -+ -+ (mask & INT_SETUP) ? " setup" : "", -+ (mask & INT_ENDPOINT0) ? " ep0" : "", -+ (mask & INT_USBRESET) ? " reset" : "", -+ (mask & INT_SUSPEND) ? " suspend" : ""); -+ *size -= t; -+ *next += t; -+} -+ -+ -+static int -+udc_proc_read(char *buffer, char **start, off_t off, int count, -+ int *eof, void *_dev) -+{ -+ char *buf = buffer; -+ struct goku_udc *dev = _dev; -+ struct goku_udc_regs *regs = dev->regs; -+ char *next = buf; -+ unsigned size = count; -+ unsigned long flags; -+ int i, t, is_usb_connected; -+ u32 tmp; -+ -+ if (off != 0) -+ return 0; -+ -+ local_irq_save(flags); -+ -+ /* basic device status */ -+ tmp = readl(®s->power_detect); -+ is_usb_connected = tmp & PW_DETECT; -+ t = snprintf(next, size, -+ "%s - %s\n" -+ "%s version: %s %s\n" -+ "Gadget driver: %s\n" -+ "Host %s, %s\n" -+ "\n", -+ pci_name(dev->pdev), driver_desc, -+ driver_name, DRIVER_VERSION, dmastr(), -+ dev->driver ? dev->driver->driver.name : "(none)", -+ is_usb_connected -+ ? ((tmp & PW_PULLUP) ? "full speed" : "powered") -+ : "disconnected", -+ ({char *tmp; -+ switch(dev->ep0state){ -+ case EP0_DISCONNECT: tmp = "ep0_disconnect"; break; -+ case EP0_IDLE: tmp = "ep0_idle"; break; -+ case EP0_IN: tmp = "ep0_in"; break; -+ case EP0_OUT: tmp = "ep0_out"; break; -+ case EP0_STATUS: tmp = "ep0_status"; break; -+ case EP0_STALL: tmp = "ep0_stall"; break; -+ case EP0_SUSPEND: tmp = "ep0_suspend"; break; -+ default: tmp = "ep0_?"; break; -+ } tmp; }) -+ ); -+ size -= t; -+ next += t; -+ -+ dump_intmask("int_status", readl(®s->int_status), &next, &size); -+ dump_intmask("int_enable", readl(®s->int_enable), &next, &size); -+ -+ if (!is_usb_connected || !dev->driver || (tmp & PW_PULLUP) == 0) -+ goto done; -+ -+ /* registers for (active) device and ep0 */ -+ t = snprintf(next, size, "\nirqs %lu\ndataset %02x " -+ "single.bcs %02x.%02x state %x addr %u\n", -+ dev->irqs, readl(®s->DataSet), -+ readl(®s->EPxSingle), readl(®s->EPxBCS), -+ readl(®s->UsbState), -+ readl(®s->address)); -+ size -= t; -+ next += t; -+ -+ tmp = readl(®s->dma_master); -+ t = snprintf(next, size, -+ "dma %03X =" EIGHTBITS "%s %s\n", tmp, -+ (tmp & MST_EOPB_DIS) ? " eopb-" : "", -+ (tmp & MST_EOPB_ENA) ? " eopb+" : "", -+ (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "", -+ (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "", -+ -+ (tmp & MST_RD_EOPB) ? " eopb" : "", -+ (tmp & MST_RD_RESET) ? " in_reset" : "", -+ (tmp & MST_WR_RESET) ? " out_reset" : "", -+ (tmp & MST_RD_ENA) ? " IN" : "", -+ -+ (tmp & MST_WR_ENA) ? " OUT" : "", -+ (tmp & MST_CONNECTION) -+ ? "ep1in/ep2out" -+ : "ep1out/ep2in"); -+ size -= t; -+ next += t; -+ -+ /* dump endpoint queues */ -+ for (i = 0; i < 4; i++) { -+ struct goku_ep *ep = &dev->ep [i]; -+ struct goku_request *req; -+ int t; -+ -+ if (i && !ep->desc) -+ continue; -+ -+ tmp = readl(ep->reg_status); -+ t = snprintf(next, size, -+ "%s %s max %u %s, irqs %lu, " -+ "status %02x (%s) " FOURBITS "\n", -+ ep->ep.name, -+ ep->is_in ? "in" : "out", -+ ep->ep.maxpacket, -+ ep->dma ? "dma" : "pio", -+ ep->irqs, -+ tmp, ({ char *s; -+ switch (tmp & EPxSTATUS_EP_MASK) { -+ case EPxSTATUS_EP_READY: -+ s = "ready"; break; -+ case EPxSTATUS_EP_DATAIN: -+ s = "packet"; break; -+ case EPxSTATUS_EP_FULL: -+ s = "full"; break; -+ case EPxSTATUS_EP_TX_ERR: // host will retry -+ s = "tx_err"; break; -+ case EPxSTATUS_EP_RX_ERR: -+ s = "rx_err"; break; -+ case EPxSTATUS_EP_BUSY: /* ep0 only */ -+ s = "busy"; break; -+ case EPxSTATUS_EP_STALL: -+ s = "stall"; break; -+ case EPxSTATUS_EP_INVALID: // these "can't happen" -+ s = "invalid"; break; -+ default: -+ s = "?"; break; -+ }; s; }), -+ (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0", -+ (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "", -+ (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "", -+ (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "" -+ ); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ -+ if (list_empty(&ep->queue)) { -+ t = snprintf(next, size, "\t(nothing queued)\n"); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ continue; -+ } -+ list_for_each_entry(req, &ep->queue, queue) { -+ if (ep->dma && req->queue.prev == &ep->queue) { -+ if (i == UDC_MSTRD_ENDPOINT) -+ tmp = readl(®s->in_dma_current); -+ else -+ tmp = readl(®s->out_dma_current); -+ tmp -= req->req.dma; -+ tmp++; -+ } else -+ tmp = req->req.actual; -+ -+ t = snprintf(next, size, -+ "\treq %p len %u/%u buf %p\n", -+ &req->req, tmp, req->req.length, -+ req->req.buf); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ } -+ } -+ -+done: -+ local_irq_restore(flags); -+ *eof = 1; -+ return count - size; -+} -+ -+#endif /* UDC_PROC_FILE */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void udc_reinit (struct goku_udc *dev) -+{ -+ static char *names [] = { "ep0", "ep1-bulk", "ep2-bulk", "ep3-bulk" }; -+ -+ unsigned i; -+ -+ INIT_LIST_HEAD (&dev->gadget.ep_list); -+ dev->gadget.ep0 = &dev->ep [0].ep; -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ dev->ep0state = EP0_DISCONNECT; -+ dev->irqs = 0; -+ -+ for (i = 0; i < 4; i++) { -+ struct goku_ep *ep = &dev->ep[i]; -+ -+ ep->num = i; -+ ep->ep.name = names[i]; -+ ep->reg_fifo = &dev->regs->ep_fifo [i]; -+ ep->reg_status = &dev->regs->ep_status [i]; -+ ep->reg_mode = &dev->regs->ep_mode[i]; -+ -+ ep->ep.ops = &goku_ep_ops; -+ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); -+ ep->dev = dev; -+ INIT_LIST_HEAD (&ep->queue); -+ -+ ep_reset(0, ep); -+ } -+ -+ dev->ep[0].reg_mode = 0; -+ dev->ep[0].ep.maxpacket = MAX_EP0_SIZE; -+ list_del_init (&dev->ep[0].ep.ep_list); -+} -+ -+static void udc_reset(struct goku_udc *dev) -+{ -+ struct goku_udc_regs *regs = dev->regs; -+ -+ writel(0, ®s->power_detect); -+ writel(0, ®s->int_enable); -+ readl(®s->int_enable); -+ dev->int_enable = 0; -+ -+ /* deassert reset, leave USB D+ at hi-Z (no pullup) -+ * don't let INT_PWRDETECT sequence begin -+ */ -+ udelay(250); -+ writel(PW_RESETB, ®s->power_detect); -+ readl(®s->int_enable); -+} -+ -+static void ep0_start(struct goku_udc *dev) -+{ -+ struct goku_udc_regs *regs = dev->regs; -+ unsigned i; -+ -+ VDBG(dev, "%s\n", __FUNCTION__); -+ -+ udc_reset(dev); -+ udc_reinit (dev); -+ //writel(MST_EOPB_ENA | MST_TIMEOUT_ENA, ®s->dma_master); -+ -+ /* hw handles set_address, set_feature, get_status; maybe more */ -+ writel( G_REQMODE_SET_INTF | G_REQMODE_GET_INTF -+ | G_REQMODE_SET_CONF | G_REQMODE_GET_CONF -+ | G_REQMODE_GET_DESC -+ | G_REQMODE_CLEAR_FEAT -+ , ®s->reqmode); -+ -+ for (i = 0; i < 4; i++) -+ dev->ep[i].irqs = 0; -+ -+ /* can't modify descriptors after writing UsbReady */ -+ for (i = 0; i < DESC_LEN; i++) -+ writel(0, ®s->descriptors[i]); -+ writel(0, ®s->UsbReady); -+ -+ /* expect ep0 requests when the host drops reset */ -+ writel(PW_RESETB | PW_PULLUP, ®s->power_detect); -+ dev->int_enable = INT_DEVWIDE | INT_EP0; -+ writel(dev->int_enable, &dev->regs->int_enable); -+ readl(®s->int_enable); -+ dev->gadget.speed = USB_SPEED_FULL; -+ dev->ep0state = EP0_IDLE; -+} -+ -+static void udc_enable(struct goku_udc *dev) -+{ -+ /* start enumeration now, or after power detect irq */ -+ if (readl(&dev->regs->power_detect) & PW_DETECT) -+ ep0_start(dev); -+ else { -+ DBG(dev, "%s\n", __FUNCTION__); -+ dev->int_enable = INT_PWRDETECT; -+ writel(dev->int_enable, &dev->regs->int_enable); -+ } -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* keeping it simple: -+ * - one bus driver, initted first; -+ * - one function driver, initted second -+ */ -+ -+static struct goku_udc *the_controller; -+ -+/* when a driver is successfully registered, it will receive -+ * control requests including set_configuration(), which enables -+ * non-control requests. then usb traffic follows until a -+ * disconnect is reported. then a host may connect again, or -+ * the driver might get unbound. -+ */ -+int usb_gadget_register_driver(struct usb_gadget_driver *driver) -+{ -+ struct goku_udc *dev = the_controller; -+ int retval; -+ -+ if (!driver -+ || driver->speed != USB_SPEED_FULL -+ || !driver->bind -+ || !driver->unbind -+ || !driver->disconnect -+ || !driver->setup) -+ return -EINVAL; -+ if (!dev) -+ return -ENODEV; -+ if (dev->driver) -+ return -EBUSY; -+ -+ /* hook up the driver */ -+ dev->driver = driver; -+ retval = driver->bind(&dev->gadget); -+ if (retval) { -+ DBG(dev, "bind to driver %s --> error %d\n", -+ driver->driver.name, retval); -+ dev->driver = 0; -+ return retval; -+ } -+ -+ /* then enable host detection and ep0; and we're ready -+ * for set_configuration as well as eventual disconnect. -+ */ -+ udc_enable(dev); -+ -+ DBG(dev, "registered gadget driver '%s'\n", driver->driver.name); -+ return 0; -+} -+EXPORT_SYMBOL(usb_gadget_register_driver); -+ -+static void -+stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver) -+{ -+ unsigned i; -+ -+ DBG (dev, "%s\n", __FUNCTION__); -+ -+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) -+ driver = 0; -+ -+ /* disconnect gadget driver after quiesceing hw and the driver */ -+ udc_reset (dev); -+ for (i = 0; i < 4; i++) -+ nuke(&dev->ep [i], -ESHUTDOWN); -+ if (driver) { -+ spin_unlock(&dev->lock); -+ driver->disconnect(&dev->gadget); -+ spin_lock(&dev->lock); -+ } -+ -+ if (dev->driver) -+ udc_enable(dev); -+} -+ -+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -+{ -+ struct goku_udc *dev = the_controller; -+ unsigned long flags; -+ -+ if (!dev) -+ return -ENODEV; -+ if (!driver || driver != dev->driver) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ dev->driver = 0; -+ stop_activity(dev, driver); -+ spin_unlock_irqrestore(&dev->lock, flags); -+ -+ driver->unbind(&dev->gadget); -+ -+ DBG(dev, "unregistered driver '%s'\n", driver->driver.name); -+ return 0; -+} -+EXPORT_SYMBOL(usb_gadget_unregister_driver); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void ep0_setup(struct goku_udc *dev) -+{ -+ struct goku_udc_regs *regs = dev->regs; -+ struct usb_ctrlrequest ctrl; -+ int tmp; -+ -+ /* read SETUP packet and enter DATA stage */ -+ ctrl.bRequestType = readl(®s->bRequestType); -+ ctrl.bRequest = readl(®s->bRequest); -+ ctrl.wValue = (readl(®s->wValueH) << 8) | readl(®s->wValueL); -+ ctrl.wIndex = (readl(®s->wIndexH) << 8) | readl(®s->wIndexL); -+ ctrl.wLength = (readl(®s->wLengthH) << 8) | readl(®s->wLengthL); -+ writel(0, ®s->SetupRecv); -+ -+ nuke(&dev->ep[0], 0); -+ dev->ep[0].stopped = 0; -+ if (likely(ctrl.bRequestType & USB_DIR_IN)) { -+ dev->ep[0].is_in = 1; -+ dev->ep0state = EP0_IN; -+ /* detect early status stages */ -+ writel(ICONTROL_STATUSNAK, &dev->regs->IntControl); -+ } else { -+ dev->ep[0].is_in = 0; -+ dev->ep0state = EP0_OUT; -+ -+ /* NOTE: CLEAR_FEATURE is done in software so that we can -+ * synchronize transfer restarts after bulk IN stalls. data -+ * won't even enter the fifo until the halt is cleared. -+ */ -+ switch (ctrl.bRequest) { -+ case USB_REQ_CLEAR_FEATURE: -+ switch (ctrl.bRequestType) { -+ case USB_RECIP_ENDPOINT: -+ tmp = ctrl.wIndex & 0x0f; -+ /* active endpoint */ -+ if (tmp > 3 || (!dev->ep[tmp].desc && tmp != 0)) -+ goto stall; -+ if (ctrl.wIndex & USB_DIR_IN) { -+ if (!dev->ep[tmp].is_in) -+ goto stall; -+ } else { -+ if (dev->ep[tmp].is_in) -+ goto stall; -+ } -+ /* endpoint halt */ -+ if (ctrl.wValue != 0) -+ goto stall; -+ if (tmp) -+ goku_clear_halt(&dev->ep[tmp]); -+succeed: -+ /* start ep0out status stage */ -+ writel(~(1<<0), ®s->EOP); -+ dev->ep[0].stopped = 1; -+ dev->ep0state = EP0_STATUS; -+ return; -+ case USB_RECIP_DEVICE: -+ /* device remote wakeup: always clear */ -+ if (ctrl.wValue != 1) -+ goto stall; -+ VDBG(dev, "clear dev remote wakeup\n"); -+ goto succeed; -+ case USB_RECIP_INTERFACE: -+ goto stall; -+ default: /* pass to gadget driver */ -+ break; -+ } -+ break; -+ default: -+ break; -+ } -+ } -+ -+#ifdef USB_TRACE -+ VDBG(dev, "SETUP %02x.%02x v%04x i%04x l%04x\n", -+ ctrl.bRequestType, ctrl.bRequest, -+ ctrl.wValue, ctrl.wIndex, ctrl.wLength); -+#endif -+ -+ /* hw wants to know when we're configured (or not) */ -+ dev->req_config = (ctrl.bRequest == USB_REQ_SET_CONFIGURATION -+ && ctrl.bRequestType == USB_RECIP_DEVICE); -+ if (unlikely(dev->req_config)) -+ dev->configured = (ctrl.wValue != 0); -+ -+ /* delegate everything to the gadget driver. -+ * it may respond after this irq handler returns. -+ */ -+ spin_unlock (&dev->lock); -+ tmp = dev->driver->setup(&dev->gadget, &ctrl); -+ spin_lock (&dev->lock); -+ if (unlikely(tmp < 0)) { -+stall: -+#ifdef USB_TRACE -+ VDBG(dev, "req %02x.%02x protocol STALL; err %d\n", -+ ctrl.bRequestType, ctrl.bRequest, tmp); -+#endif -+ command(regs, COMMAND_STALL, 0); -+ dev->ep[0].stopped = 1; -+ dev->ep0state = EP0_STALL; -+ } -+ -+ /* expect at least one data or status stage irq */ -+} -+ -+#define ACK(irqbit) { \ -+ stat &= ~irqbit; \ -+ writel(~irqbit, ®s->int_status); \ -+ handled = 1; \ -+ } -+ -+static irqreturn_t goku_irq(int irq, void *_dev, struct pt_regs *r) -+{ -+ struct goku_udc *dev = _dev; -+ struct goku_udc_regs *regs = dev->regs; -+ struct goku_ep *ep; -+ u32 stat, handled = 0; -+ unsigned i, rescans = 5; -+ -+ spin_lock(&dev->lock); -+ -+rescan: -+ stat = readl(®s->int_status) & dev->int_enable; -+ if (!stat) -+ goto done; -+ dev->irqs++; -+ -+ /* device-wide irqs */ -+ if (unlikely(stat & INT_DEVWIDE)) { -+ if (stat & INT_SYSERROR) { -+ ERROR(dev, "system error\n"); -+ stop_activity(dev, dev->driver); -+ stat = 0; -+ handled = 1; -+ // FIXME have a neater way to prevent re-enumeration -+ dev->driver = 0; -+ goto done; -+ } -+ if (stat & INT_PWRDETECT) { -+ writel(~stat, ®s->int_status); -+ if (readl(&dev->regs->power_detect) & PW_DETECT) { -+ VDBG(dev, "connect\n"); -+ ep0_start(dev); -+ } else { -+ DBG(dev, "disconnect\n"); -+ if (dev->gadget.speed == USB_SPEED_FULL) -+ stop_activity(dev, dev->driver); -+ dev->ep0state = EP0_DISCONNECT; -+ dev->int_enable = INT_DEVWIDE; -+ writel(dev->int_enable, &dev->regs->int_enable); -+ } -+ stat = 0; -+ handled = 1; -+ goto done; -+ } -+ if (stat & INT_SUSPEND) { -+ ACK(INT_SUSPEND); -+ if (readl(®s->ep_status[0]) & EPxSTATUS_SUSPEND) { -+ switch (dev->ep0state) { -+ case EP0_DISCONNECT: -+ case EP0_SUSPEND: -+ goto pm_next; -+ default: -+ break; -+ } -+ DBG(dev, "USB suspend\n"); -+ dev->ep0state = EP0_SUSPEND; -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN -+ && dev->driver -+ && dev->driver->suspend) { -+ spin_unlock(&dev->lock); -+ dev->driver->suspend(&dev->gadget); -+ spin_lock(&dev->lock); -+ } -+ } else { -+ if (dev->ep0state != EP0_SUSPEND) { -+ DBG(dev, "bogus USB resume %d\n", -+ dev->ep0state); -+ goto pm_next; -+ } -+ DBG(dev, "USB resume\n"); -+ dev->ep0state = EP0_IDLE; -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN -+ && dev->driver -+ && dev->driver->resume) { -+ spin_unlock(&dev->lock); -+ dev->driver->resume(&dev->gadget); -+ spin_lock(&dev->lock); -+ } -+ } -+ } -+pm_next: -+ if (stat & INT_USBRESET) { /* hub reset done */ -+ ACK(INT_USBRESET); -+ INFO(dev, "USB reset done, gadget %s\n", -+ dev->driver->driver.name); -+ } -+ // and INT_ERR on some endpoint's crc/bitstuff/... problem -+ } -+ -+ /* progress ep0 setup, data, or status stages. -+ * no transition {EP0_STATUS, EP0_STALL} --> EP0_IDLE; saves irqs -+ */ -+ if (stat & INT_SETUP) { -+ ACK(INT_SETUP); -+ dev->ep[0].irqs++; -+ ep0_setup(dev); -+ } -+ if (stat & INT_STATUSNAK) { -+ ACK(INT_STATUSNAK|INT_ENDPOINT0); -+ if (dev->ep0state == EP0_IN) { -+ ep = &dev->ep[0]; -+ ep->irqs++; -+ nuke(ep, 0); -+ writel(~(1<<0), ®s->EOP); -+ dev->ep0state = EP0_STATUS; -+ } -+ } -+ if (stat & INT_ENDPOINT0) { -+ ACK(INT_ENDPOINT0); -+ ep = &dev->ep[0]; -+ ep->irqs++; -+ pio_advance(ep); -+ } -+ -+ /* dma completion */ -+ if (stat & INT_MSTRDEND) { /* IN */ -+ ACK(INT_MSTRDEND); -+ ep = &dev->ep[UDC_MSTRD_ENDPOINT]; -+ ep->irqs++; -+ dma_advance(dev, ep); -+ } -+ if (stat & INT_MSTWREND) { /* OUT */ -+ ACK(INT_MSTWREND); -+ ep = &dev->ep[UDC_MSTWR_ENDPOINT]; -+ ep->irqs++; -+ dma_advance(dev, ep); -+ } -+ if (stat & INT_MSTWRTMOUT) { /* OUT */ -+ ACK(INT_MSTWRTMOUT); -+ ep = &dev->ep[UDC_MSTWR_ENDPOINT]; -+ ep->irqs++; -+ ERROR(dev, "%s write timeout ?\n", ep->ep.name); -+ // reset dma? then dma_advance() -+ } -+ -+ /* pio */ -+ for (i = 1; i < 4; i++) { -+ u32 tmp = INT_EPxDATASET(i); -+ -+ if (!(stat & tmp)) -+ continue; -+ ep = &dev->ep[i]; -+ pio_advance(ep); -+ if (list_empty (&ep->queue)) -+ pio_irq_disable(dev, regs, i); -+ stat &= ~tmp; -+ handled = 1; -+ ep->irqs++; -+ } -+ -+ if (rescans--) -+ goto rescan; -+ -+done: -+ (void)readl(®s->int_enable); -+ spin_unlock(&dev->lock); -+ if (stat) -+ DBG(dev, "unhandled irq status: %05x (%05x, %05x)\n", stat, -+ readl(®s->int_status), dev->int_enable); -+ return IRQ_RETVAL(handled); -+} -+ -+#undef ACK -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* tear down the binding between this driver and the pci device */ -+ -+static void goku_remove(struct pci_dev *pdev) -+{ -+ struct goku_udc *dev = pci_get_drvdata(pdev); -+ -+ DBG(dev, "%s\n", __FUNCTION__); -+ /* start with the driver above us */ -+ if (dev->driver) { -+ /* should have been done already by driver model core */ -+ WARN(dev, "pci remove, driver '%s' is still registered\n", -+ dev->driver->driver.name); -+ usb_gadget_unregister_driver(dev->driver); -+ } -+ -+#ifdef UDC_PROC_FILE -+ remove_proc_entry(proc_node_name, NULL); -+#endif -+ if (dev->regs) -+ udc_reset(dev); -+ if (dev->got_irq) -+ free_irq(pdev->irq, dev); -+ if (dev->regs) -+ iounmap(dev->regs); -+ if (dev->got_region) -+ release_mem_region(pci_resource_start (pdev, 0), -+ pci_resource_len (pdev, 0)); -+ if (dev->enabled) -+ pci_disable_device(pdev); -+ -+ pci_set_drvdata(pdev, 0); -+ dev->regs = 0; -+ the_controller = 0; -+ -+ INFO(dev, "unbind\n"); -+} -+ -+/* wrap this driver around the specified pci device, but -+ * don't respond over USB until a gadget driver binds to us. -+ */ -+ -+static int goku_probe(struct pci_dev *pdev, const struct pci_device_id *id) -+{ -+ struct goku_udc *dev = 0; -+ unsigned long resource, len; -+ void *base = 0; -+ int retval; -+ char buf [8], *bufp; -+ -+ /* if you want to support more than one controller in a system, -+ * usb_gadget_driver_{register,unregister}() must change. -+ */ -+ if (the_controller) { -+ WARN(dev, "ignoring %s\n", pci_name(pdev)); -+ return -EBUSY; -+ } -+ if (!pdev->irq) { -+ printk(KERN_ERR "Check PCI %s IRQ setup!\n", pci_name(pdev)); -+ retval = -ENODEV; -+ goto done; -+ } -+ -+ /* alloc, and start init */ -+ dev = kmalloc (sizeof *dev, SLAB_KERNEL); -+ if (dev == NULL){ -+ pr_debug("enomem %s\n", pci_name(pdev)); -+ retval = -ENOMEM; -+ goto done; -+ } -+ -+ memset(dev, 0, sizeof *dev); -+ spin_lock_init(&dev->lock); -+ dev->pdev = pdev; -+ dev->gadget.ops = &goku_ops; -+ -+ /* the "gadget" abstracts/virtualizes the controller */ -+ dev->gadget.dev.bus_id = "gadget"; -+ dev->gadget.name = driver_name; -+ -+ /* now all the pci goodies ... */ -+ retval = pci_enable_device(pdev); -+ if (retval < 0) { -+ DBG(dev, "can't enable, %d\n", retval); -+ goto done; -+ } -+ dev->enabled = 1; -+ -+ resource = pci_resource_start(pdev, 0); -+ len = pci_resource_len(pdev, 0); -+ if (!request_mem_region(resource, len, driver_name)) { -+ DBG(dev, "controller already in use\n"); -+ retval = -EBUSY; -+ goto done; -+ } -+ dev->got_region = 1; -+ -+ base = ioremap_nocache(resource, len); -+ if (base == NULL) { -+ DBG(dev, "can't map memory\n"); -+ retval = -EFAULT; -+ goto done; -+ } -+ dev->regs = (struct goku_udc_regs *) base; -+ -+ pci_set_drvdata(pdev, dev); -+ INFO(dev, "%s\n", driver_desc); -+ INFO(dev, "version: " DRIVER_VERSION " %s\n", dmastr()); -+#ifndef __sparc__ -+ snprintf(buf, sizeof buf, "%d", pdev->irq); -+ bufp = buf; -+#else -+ bufp = __irq_itoa(pdev->irq); -+#endif -+ INFO(dev, "irq %s, pci mem %p\n", bufp, base); -+ -+ /* init to known state, then setup irqs */ -+ udc_reset(dev); -+ udc_reinit (dev); -+ if (request_irq(pdev->irq, goku_irq, SA_SHIRQ/*|SA_SAMPLE_RANDOM*/, -+ driver_name, dev) != 0) { -+ DBG(dev, "request interrupt %s failed\n", bufp); -+ retval = -EBUSY; -+ goto done; -+ } -+ dev->got_irq = 1; -+ if (use_dma) -+ pci_set_master(pdev); -+ -+ -+#ifdef UDC_PROC_FILE -+ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); -+#endif -+ -+ /* done */ -+ the_controller = dev; -+ -+ return 0; -+ -+done: -+ if (dev) -+ goku_remove (pdev); -+ return retval; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct pci_device_id pci_ids [] = { { -+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), -+ .class_mask = ~0, -+ .vendor = 0x102f, /* Toshiba */ -+ .device = 0x0107, /* this UDC */ -+ .subvendor = PCI_ANY_ID, -+ .subdevice = PCI_ANY_ID, -+ -+}, { /* end: all zeroes */ } -+}; -+MODULE_DEVICE_TABLE (pci, pci_ids); -+ -+static struct pci_driver goku_pci_driver = { -+ .name = (char *) driver_name, -+ .id_table = pci_ids, -+ -+ .probe = goku_probe, -+ .remove = goku_remove, -+ -+ /* FIXME add power management support */ -+}; -+ -+static int __init init (void) -+{ -+ return pci_module_init (&goku_pci_driver); -+} -+module_init (init); -+ -+static void __exit cleanup (void) -+{ -+ pci_unregister_driver (&goku_pci_driver); -+} -+module_exit (cleanup); -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/goku_udc.h kernel/drivers/usb/gadget/goku_udc.h ---- /tmp/kernel/drivers/usb/gadget/goku_udc.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/goku_udc.h 2005-04-22 17:53:19.443538050 +0200 -@@ -0,0 +1,321 @@ -+/* -+ * Toshiba TC86C001 ("Goku-S") USB Device Controller driver -+ * -+ * Copyright (C) 2000-2002 Lineo -+ * by Stuart Lynne, Tom Rushworth, and Bruce Balden -+ * Copyright (C) 2002 Toshiba Corporation -+ * Copyright (C) 2003 MontaVista Software (source@mvista.com) -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+/* -+ * PCI BAR 0 points to these registers. -+ */ -+struct goku_udc_regs { -+ /* irq management */ -+ u32 int_status; /* 0x000 */ -+ u32 int_enable; -+#define INT_SUSPEND 0x00001 /* or resume */ -+#define INT_USBRESET 0x00002 -+#define INT_ENDPOINT0 0x00004 -+#define INT_SETUP 0x00008 -+#define INT_STATUS 0x00010 -+#define INT_STATUSNAK 0x00020 -+#define INT_EPxDATASET(n) (0x00020 << (n)) /* 0 < n < 4 */ -+# define INT_EP1DATASET 0x00040 -+# define INT_EP2DATASET 0x00080 -+# define INT_EP3DATASET 0x00100 -+#define INT_EPnNAK(n) (0x00100 < (n)) /* 0 < n < 4 */ -+# define INT_EP1NAK 0x00200 -+# define INT_EP2NAK 0x00400 -+# define INT_EP3NAK 0x00800 -+#define INT_SOF 0x01000 -+#define INT_ERR 0x02000 -+#define INT_MSTWRSET 0x04000 -+#define INT_MSTWREND 0x08000 -+#define INT_MSTWRTMOUT 0x10000 -+#define INT_MSTRDEND 0x20000 -+#define INT_SYSERROR 0x40000 -+#define INT_PWRDETECT 0x80000 -+ -+#define INT_DEVWIDE (INT_PWRDETECT|INT_SYSERROR/*|INT_ERR*/|INT_USBRESET|INT_SUSPEND) -+#define INT_EP0 (INT_SETUP|INT_ENDPOINT0/*|INT_STATUS*/|INT_STATUSNAK) -+ -+ u32 dma_master; -+#define MST_EOPB_DIS 0x0800 -+#define MST_EOPB_ENA 0x0400 -+#define MST_TIMEOUT_DIS 0x0200 -+#define MST_TIMEOUT_ENA 0x0100 -+#define MST_RD_EOPB 0x0080 /* write-only */ -+#define MST_RD_RESET 0x0040 -+#define MST_WR_RESET 0x0020 -+#define MST_RD_ENA 0x0004 /* 1:start, 0:ignore */ -+#define MST_WR_ENA 0x0002 /* 1:start, 0:ignore */ -+#define MST_CONNECTION 0x0001 /* 0 for ep1out/ep2in */ -+ -+#define MST_R_BITS (MST_EOPB_DIS|MST_EOPB_ENA \ -+ |MST_RD_ENA|MST_RD_RESET) -+#define MST_W_BITS (MST_TIMEOUT_DIS|MST_TIMEOUT_ENA \ -+ |MST_WR_ENA|MST_WR_RESET) -+#define MST_RW_BITS (MST_R_BITS|MST_W_BITS \ -+ |MST_CONNECTION) -+ -+/* these values assume (dma_master & MST_CONNECTION) == 0 */ -+#define UDC_MSTWR_ENDPOINT 1 -+#define UDC_MSTRD_ENDPOINT 2 -+ -+ /* dma master write */ -+ u32 out_dma_start; -+ u32 out_dma_end; -+ u32 out_dma_current; -+ -+ /* dma master read */ -+ u32 in_dma_start; -+ u32 in_dma_end; -+ u32 in_dma_current; -+ -+ u32 power_detect; -+#define PW_DETECT 0x04 -+#define PW_RESETB 0x02 -+#define PW_PULLUP 0x01 -+ -+ u8 _reserved0 [0x1d8]; -+ -+ /* endpoint registers */ -+ u32 ep_fifo [4]; /* 0x200 */ -+ u8 _reserved1 [0x10]; -+ u32 ep_mode [4]; /* only 1-3 valid */ -+ u8 _reserved2 [0x10]; -+ -+ u32 ep_status [4]; -+#define EPxSTATUS_TOGGLE 0x40 -+#define EPxSTATUS_SUSPEND 0x20 -+#define EPxSTATUS_EP_MASK (0x07<<2) -+# define EPxSTATUS_EP_READY (0<<2) -+# define EPxSTATUS_EP_DATAIN (1<<2) -+# define EPxSTATUS_EP_FULL (2<<2) -+# define EPxSTATUS_EP_TX_ERR (3<<2) -+# define EPxSTATUS_EP_RX_ERR (4<<2) -+# define EPxSTATUS_EP_BUSY (5<<2) -+# define EPxSTATUS_EP_STALL (6<<2) -+# define EPxSTATUS_EP_INVALID (7<<2) -+#define EPxSTATUS_FIFO_DISABLE 0x02 -+#define EPxSTATUS_STAGE_ERROR 0x01 -+ -+ u8 _reserved3 [0x10]; -+ u32 EPxSizeLA[4]; -+#define PACKET_ACTIVE (1<<7) -+#define DATASIZE 0x7f -+ u8 _reserved3a [0x10]; -+ u32 EPxSizeLB[4]; /* only 1,2 valid */ -+ u8 _reserved3b [0x10]; -+ u32 EPxSizeHA[4]; /* only 1-3 valid */ -+ u8 _reserved3c [0x10]; -+ u32 EPxSizeHB[4]; /* only 1,2 valid */ -+ u8 _reserved4[0x30]; -+ -+ /* SETUP packet contents */ -+ u32 bRequestType; /* 0x300 */ -+ u32 bRequest; -+ u32 wValueL; -+ u32 wValueH; -+ u32 wIndexL; -+ u32 wIndexH; -+ u32 wLengthL; -+ u32 wLengthH; -+ -+ /* command interaction/handshaking */ -+ u32 SetupRecv; /* 0x320 */ -+ u32 CurrConfig; -+ u32 StdRequest; -+ u32 Request; -+ u32 DataSet; -+#define DATASET_A(epnum) (1<<(2*(epnum))) -+#define DATASET_B(epnum) (2<<(2*(epnum))) -+#define DATASET_AB(epnum) (3<<(2*(epnum))) -+ u8 _reserved5[4]; -+ -+ u32 UsbState; -+#define USBSTATE_CONFIGURED 0x04 -+#define USBSTATE_ADDRESSED 0x02 -+#define USBSTATE_DEFAULT 0x01 -+ -+ u32 EOP; -+ -+ u32 Command; /* 0x340 */ -+#define COMMAND_SETDATA0 2 -+#define COMMAND_RESET 3 -+#define COMMAND_STALL 4 -+#define COMMAND_INVALID 5 -+#define COMMAND_FIFO_DISABLE 7 -+#define COMMAND_FIFO_ENABLE 8 -+#define COMMAND_INIT_DESCRIPTOR 9 -+#define COMMAND_FIFO_CLEAR 10 /* also stall */ -+#define COMMAND_STALL_CLEAR 11 -+#define COMMAND_EP(n) ((n) << 4) -+ -+ u32 EPxSingle; -+ u8 _reserved6[4]; -+ u32 EPxBCS; -+ u8 _reserved7[8]; -+ u32 IntControl; -+#define ICONTROL_STATUSNAK 1 -+ u8 _reserved8[4]; -+ -+ u32 reqmode; // 0x360 standard request mode, low 8 bits -+#define G_REQMODE_SET_INTF (1<<7) -+#define G_REQMODE_GET_INTF (1<<6) -+#define G_REQMODE_SET_CONF (1<<5) -+#define G_REQMODE_GET_CONF (1<<4) -+#define G_REQMODE_GET_DESC (1<<3) -+#define G_REQMODE_SET_FEAT (1<<2) -+#define G_REQMODE_CLEAR_FEAT (1<<1) -+#define G_REQMODE_GET_STATUS (1<<0) -+ -+ u32 ReqMode; -+ u8 _reserved9[0x18]; -+ u32 PortStatus; /* 0x380 */ -+ u8 _reserved10[8]; -+ u32 address; -+ u32 buff_test; -+ u8 _reserved11[4]; -+ u32 UsbReady; -+ u8 _reserved12[4]; -+ u32 SetDescStall; /* 0x3a0 */ -+ u8 _reserved13[0x45c]; -+ -+ /* hardware could handle limited GET_DESCRIPTOR duties */ -+#define DESC_LEN 0x80 -+ u32 descriptors[DESC_LEN]; /* 0x800 */ -+ u8 _reserved14[0x600]; -+ -+} __attribute__ ((packed)); -+ -+#define MAX_FIFO_SIZE 64 -+#define MAX_EP0_SIZE 8 /* ep0 fifo is bigger, though */ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* DRIVER DATA STRUCTURES and UTILITIES */ -+ -+struct goku_ep { -+ struct usb_ep ep; -+ struct goku_udc *dev; -+ unsigned long irqs; -+ -+ unsigned num:8, -+ dma:1, -+ is_in:1, -+ stopped:1; -+ -+ /* analogous to a host-side qh */ -+ struct list_head queue; -+ const struct usb_endpoint_descriptor *desc; -+ -+ u32 *reg_fifo; -+ u32 *reg_mode; -+ u32 *reg_status; -+}; -+ -+struct goku_request { -+ struct usb_request req; -+ struct list_head queue; -+ -+ unsigned mapped:1; -+}; -+ -+enum ep0state { -+ EP0_DISCONNECT, /* no host */ -+ EP0_IDLE, /* between STATUS ack and SETUP report */ -+ EP0_IN, EP0_OUT, /* data stage */ -+ EP0_STATUS, /* status stage */ -+ EP0_STALL, /* data or status stages */ -+ EP0_SUSPEND, /* usb suspend */ -+}; -+ -+struct goku_udc { -+ /* each pci device provides one gadget, several endpoints */ -+ struct usb_gadget gadget; -+ spinlock_t lock; -+ struct goku_ep ep[4]; -+ struct usb_gadget_driver *driver; -+ -+ enum ep0state ep0state; -+ unsigned got_irq:1, -+ got_region:1, -+ req_config:1, -+ configured:1, -+ enabled:1; -+ -+ /* pci state used to access those endpoints */ -+ struct pci_dev *pdev; -+ struct goku_udc_regs *regs; -+ u32 int_enable; -+ -+ /* statistics... */ -+ unsigned long irqs; -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define xprintk(dev,level,fmt,args...) \ -+ printk(level "%s %s: " fmt , driver_name , \ -+ pci_name(dev->pdev) , ## args) -+ -+#ifdef DEBUG -+#define DBG(dev,fmt,args...) \ -+ xprintk(dev , KERN_DEBUG , fmt , ## args) -+#else -+#define DBG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+#ifdef VERBOSE -+#define VDBG DBG -+#else -+#define VDBG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* VERBOSE */ -+ -+#define ERROR(dev,fmt,args...) \ -+ xprintk(dev , KERN_ERR , fmt , ## args) -+#define WARN(dev,fmt,args...) \ -+ xprintk(dev , KERN_WARNING , fmt , ## args) -+#define INFO(dev,fmt,args...) \ -+ xprintk(dev , KERN_INFO , fmt , ## args) -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* 2.5 stuff that's sometimes missing in 2.4 */ -+ -+#ifndef container_of -+#define container_of list_entry -+#endif -+ -+#ifndef likely -+#define likely(x) (x) -+#define unlikely(x) (x) -+#endif -+ -+#ifndef BUG_ON -+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) -+#endif -+ -+#ifndef WARN_ON -+#define WARN_ON(x) do { } while (0) -+#endif -+ -+#ifndef IRQ_NONE -+typedef void irqreturn_t; -+#define IRQ_NONE -+#define IRQ_HANDLED -+#define IRQ_RETVAL(x) -+#endif -+ -+#ifndef pci_name -+#define pci_name(pdev) ((pdev)->slot_name) -+#endif -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/gserial.c kernel/drivers/usb/gadget/gserial.c ---- /tmp/kernel/drivers/usb/gadget/gserial.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/gserial.c 2005-04-22 17:53:19.450536911 +0200 -@@ -0,0 +1,2301 @@ -+/* -+ * g_serial.c -- USB gadget serial driver -+ * -+ * $Id: gserial.c,v 1.17 2003/10/01 06:31:57 borchers Exp $ -+ * -+ * Copyright 2003 (c) Al Borchers (alborchers@steinerpoint.com) -+ * -+ * This code is based in part on the Gadget Zero driver, which -+ * is Copyright (C) 2003 by David Brownell, all rights reserved. -+ * -+ * This code also borrows from usbserial.c, which is -+ * Copyright (C) 1999 - 2002 Greg Kroah-Hartman (greg@kroah.com) -+ * Copyright (c) 2000 Peter Berger (pberger@brimson.com) -+ * Copyright (c) 2000 Al Borchers (alborchers@steinerpoint.com) -+ * -+ * This software is distributed under the terms of the GNU General -+ * Public License ("GPL") as published by the Free Software Foundation, -+ * either version 2 of that License or (at your option) any later version. -+ * -+ */ -+ -+#ifndef __KERNEL__ -+#define __KERNEL__ -+#endif -+ -+#ifndef MODULE -+#define MODULE -+#endif -+ -+ -+/* Includes */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/delay.h> -+#include <linux/ioport.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/smp_lock.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+#include <linux/uts.h> -+#include <linux/version.h> -+#include <linux/wait.h> -+#include <linux/list.h> -+#include <linux/proc_fs.h> -+#include <linux/tty_flip.h> -+ -+#include <asm/byteorder.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/unaligned.h> -+#include <asm/uaccess.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include "gadget_chips.h" -+ -+ -+/* Wait Cond */ -+ -+#define __wait_cond_interruptible(wq, condition, lock, flags, ret) \ -+do { \ -+ wait_queue_t __wait; \ -+ init_waitqueue_entry(&__wait, current); \ -+ \ -+ add_wait_queue(&wq, &__wait); \ -+ for (;;) { \ -+ set_current_state(TASK_INTERRUPTIBLE); \ -+ if (condition) \ -+ break; \ -+ if (!signal_pending(current)) { \ -+ spin_unlock_irqrestore(lock, flags); \ -+ schedule(); \ -+ spin_lock_irqsave(lock, flags); \ -+ continue; \ -+ } \ -+ ret = -ERESTARTSYS; \ -+ break; \ -+ } \ -+ current->state = TASK_RUNNING; \ -+ remove_wait_queue(&wq, &__wait); \ -+} while (0) -+ -+#define wait_cond_interruptible(wq, condition, lock, flags) \ -+({ \ -+ int __ret = 0; \ -+ if (!(condition)) \ -+ __wait_cond_interruptible(wq, condition, lock, flags, \ -+ __ret); \ -+ __ret; \ -+}) -+ -+#define __wait_cond_interruptible_timeout(wq, condition, lock, flags, \ -+ timeout, ret) \ -+do { \ -+ signed long __timeout = timeout; \ -+ wait_queue_t __wait; \ -+ init_waitqueue_entry(&__wait, current); \ -+ \ -+ add_wait_queue(&wq, &__wait); \ -+ for (;;) { \ -+ set_current_state(TASK_INTERRUPTIBLE); \ -+ if (__timeout == 0) \ -+ break; \ -+ if (condition) \ -+ break; \ -+ if (!signal_pending(current)) { \ -+ spin_unlock_irqrestore(lock, flags); \ -+ __timeout = schedule_timeout(__timeout); \ -+ spin_lock_irqsave(lock, flags); \ -+ continue; \ -+ } \ -+ ret = -ERESTARTSYS; \ -+ break; \ -+ } \ -+ current->state = TASK_RUNNING; \ -+ remove_wait_queue(&wq, &__wait); \ -+} while (0) -+ -+#define wait_cond_interruptible_timeout(wq, condition, lock, flags, \ -+ timeout) \ -+({ \ -+ int __ret = 0; \ -+ if (!(condition)) \ -+ __wait_cond_interruptible_timeout(wq, condition, lock, \ -+ flags, timeout, __ret); \ -+ __ret; \ -+}) -+ -+ -+/* Defines */ -+ -+#define GS_VERSION_STR "v1.0" -+#define GS_VERSION_NUM 0x0100 -+ -+#define GS_LONG_NAME "Gadget Serial" -+#define GS_SHORT_NAME "g_serial" -+ -+#define GS_MAJOR 127 -+#define GS_MINOR_START 0 -+ -+#define GS_NUM_PORTS 16 -+ -+#define GS_NUM_CONFIGS 1 -+#define GS_NO_CONFIG_ID 0 -+#define GS_BULK_CONFIG_ID 2 -+ -+#define GS_NUM_INTERFACES 1 -+#define GS_INTERFACE_ID 0 -+#define GS_ALT_INTERFACE_ID 0 -+ -+#define GS_NUM_ENDPOINTS 2 -+ -+#define GS_MAX_DESC_LEN 256 -+ -+#define GS_DEFAULT_READ_Q_SIZE 32 -+#define GS_DEFAULT_WRITE_Q_SIZE 32 -+ -+#define GS_DEFAULT_WRITE_BUF_SIZE 8192 -+#define GS_TMP_BUF_SIZE 8192 -+ -+#define GS_CLOSE_TIMEOUT 15 -+ -+/* debug macro */ -+#if G_SERIAL_DEBUG -+ -+static int debug = G_SERIAL_DEBUG; -+ -+#define gs_debug(format, arg...) \ -+ do { if(debug) printk( KERN_DEBUG format, ## arg ); } while(0) -+#define gs_debug_level(level, format, arg...) \ -+ do { if(debug>=level) printk( KERN_DEBUG format, ## arg ); } while(0) -+ -+#else -+ -+#define gs_debug(format, arg...) \ -+ do { } while(0) -+#define gs_debug_level(level, format, arg...) \ -+ do { } while(0) -+ -+#endif /* G_SERIAL_DEBUG */ -+ -+ -+/* Thanks to NetChip Technologies for donating this product ID. -+ * -+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! -+ * Instead: allocate your own, using normal USB-IF procedures. -+ */ -+#define GS_VENDOR_ID 0x0525 /* NetChip */ -+#define GS_PRODUCT_ID 0xa4a6 /* Linux-USB Serial Gadget */ -+ -+ -+/* Structures */ -+ -+struct gs_dev; -+ -+/* circular buffer */ -+struct gs_buf { -+ unsigned int buf_size; -+ char *buf_buf; -+ char *buf_get; -+ char *buf_put; -+}; -+ -+/* list of requests */ -+struct gs_req_entry { -+ struct list_head re_entry; -+ struct usb_request *re_req; -+}; -+ -+/* the port structure holds info for each port, one for each minor number */ -+struct gs_port { -+ struct gs_dev *port_dev; /* pointer to device struct */ -+ struct tty_struct *port_tty; /* pointer to tty struct */ -+ spinlock_t port_lock; -+ int port_num; -+ int port_open_count; -+ int port_in_use; /* open/close in progress */ -+ wait_queue_head_t port_write_wait;/* waiting to write */ -+ struct gs_buf *port_write_buf; -+}; -+ -+/* the device structure holds info for the USB device */ -+struct gs_dev { -+ struct usb_gadget *dev_gadget; /* gadget device pointer */ -+ spinlock_t dev_lock; /* lock for set/reset config */ -+ int dev_config; /* configuration number */ -+ struct usb_ep *dev_in_ep; /* address of in endpoint */ -+ struct usb_ep *dev_out_ep; /* address of out endpoint */ -+ struct usb_request *dev_ctrl_req; /* control request */ -+ struct list_head dev_req_list; /* list of write requests */ -+ int dev_sched_port; /* round robin port scheduled */ -+ struct gs_port *dev_port[GS_NUM_PORTS]; /* the ports */ -+}; -+ -+ -+/* Functions */ -+ -+/* module */ -+static int __init gs_module_init( void ); -+static void __exit gs_module_exit( void ); -+ -+/* tty driver */ -+static int gs_open( struct tty_struct *tty, struct file *file ); -+static void gs_close( struct tty_struct *tty, struct file *file ); -+static int gs_write( struct tty_struct *tty, int from_user, -+ const unsigned char *buf, int count ); -+static void gs_put_char( struct tty_struct *tty, unsigned char ch ); -+static void gs_flush_chars( struct tty_struct *tty ); -+static int gs_write_room( struct tty_struct *tty ); -+static int gs_chars_in_buffer( struct tty_struct *tty ); -+static void gs_throttle( struct tty_struct * tty ); -+static void gs_unthrottle( struct tty_struct * tty ); -+static void gs_break( struct tty_struct *tty, int break_state ); -+static int gs_ioctl( struct tty_struct *tty, struct file *file, -+ unsigned int cmd, unsigned long arg ); -+static void gs_set_termios( struct tty_struct *tty, struct termios *old ); -+static int gs_read_proc( char *page, char **start, off_t off, int count, -+ int *eof, void *data ); -+ -+static int gs_send( struct gs_dev *dev ); -+static int gs_send_packet( struct gs_dev *dev, char *packet, -+ unsigned int size ); -+static int gs_recv_packet( struct gs_dev *dev, char *packet, -+ unsigned int size ); -+static void gs_read_complete( struct usb_ep *ep, struct usb_request *req ); -+static void gs_write_complete( struct usb_ep *ep, struct usb_request *req ); -+ -+/* gadget driver */ -+static int gs_bind( struct usb_gadget *gadget ); -+static void gs_unbind( struct usb_gadget *gadget ); -+static int gs_setup( struct usb_gadget *gadget, -+ const struct usb_ctrlrequest *ctrl ); -+static void gs_setup_complete( struct usb_ep *ep, struct usb_request *req ); -+static void gs_disconnect( struct usb_gadget *gadget ); -+static int gs_set_config( struct gs_dev *dev, unsigned config ); -+static void gs_reset_config( struct gs_dev *dev ); -+static int gs_build_config_desc( u8 *buf, enum usb_device_speed speed, -+ u8 type, unsigned int index ); -+ -+static struct usb_request *gs_alloc_req( struct usb_ep *ep, unsigned int len, -+ int kmalloc_flags ); -+static void gs_free_req( struct usb_ep *ep, struct usb_request *req ); -+ -+static struct gs_req_entry *gs_alloc_req_entry( struct usb_ep *ep, unsigned len, -+ int kmalloc_flags ); -+static void gs_free_req_entry( struct usb_ep *ep, struct gs_req_entry *req ); -+ -+static int gs_alloc_ports( struct gs_dev *dev, int kmalloc_flags ); -+static void gs_free_ports( struct gs_dev *dev ); -+ -+/* circular buffer */ -+static struct gs_buf *gs_buf_alloc( unsigned int size, int kmalloc_flags ); -+static void gs_buf_free( struct gs_buf *gb ); -+static void gs_buf_clear( struct gs_buf *gb ); -+static unsigned int gs_buf_data_avail( struct gs_buf *gb ); -+static unsigned int gs_buf_space_avail( struct gs_buf *gb ); -+static unsigned int gs_buf_put( struct gs_buf *gb, const char *buf, -+ unsigned int count ); -+static unsigned int gs_buf_get( struct gs_buf *gb, char *buf, -+ unsigned int count ); -+ -+/* external functions */ -+extern int net2280_set_fifo_mode(struct usb_gadget *gadget, int mode); -+ -+ -+/* Globals */ -+ -+static struct gs_dev *gs_device; -+ -+static const char *EP_IN_NAME; -+static const char *EP_OUT_NAME; -+ -+static struct semaphore gs_open_close_sem[GS_NUM_PORTS]; -+ -+static unsigned int read_q_size = GS_DEFAULT_READ_Q_SIZE; -+static unsigned int write_q_size = GS_DEFAULT_WRITE_Q_SIZE; -+ -+static unsigned int write_buf_size = GS_DEFAULT_WRITE_BUF_SIZE; -+ -+static unsigned char gs_tmp_buf[GS_TMP_BUF_SIZE]; -+static struct semaphore gs_tmp_buf_sem; -+ -+/* tty variables */ -+static int gs_refcount; -+static struct tty_struct *gs_tty[GS_NUM_PORTS]; -+static struct termios *gs_termios[GS_NUM_PORTS]; -+static struct termios *gs_termios_locked[GS_NUM_PORTS]; -+ -+/* tty driver struct */ -+static struct tty_driver gs_tty_driver = { -+ .magic = TTY_DRIVER_MAGIC, -+ .driver_name = GS_SHORT_NAME, -+ .name = "ttygs", -+ .major = GS_MAJOR, -+ .minor_start = GS_MINOR_START, -+ .num = GS_NUM_PORTS, -+ .type = TTY_DRIVER_TYPE_SERIAL, -+ .subtype = SERIAL_TYPE_NORMAL, -+ .flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, -+ .refcount = &gs_refcount, -+ .table = gs_tty, -+ .termios = gs_termios, -+ .termios_locked = gs_termios_locked, -+ -+ .open = gs_open, -+ .close = gs_close, -+ .write = gs_write, -+ .put_char = gs_put_char, -+ .flush_chars = gs_flush_chars, -+ .write_room = gs_write_room, -+ .ioctl = gs_ioctl, -+ .set_termios = gs_set_termios, -+ .throttle = gs_throttle, -+ .unthrottle = gs_unthrottle, -+ .break_ctl = gs_break, -+ .chars_in_buffer = gs_chars_in_buffer, -+ .read_proc = gs_read_proc, -+}; -+ -+/* gadget driver struct */ -+static struct usb_gadget_driver gs_gadget_driver = { -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ .speed = USB_SPEED_HIGH, -+#else -+ .speed = USB_SPEED_FULL, -+#endif -+ .function = GS_LONG_NAME, -+ .bind = gs_bind, -+ .unbind = gs_unbind, -+ .setup = gs_setup, -+ .disconnect = gs_disconnect, -+ .driver = { -+ .name = GS_SHORT_NAME, -+ /* .shutdown = ... */ -+ /* .suspend = ... */ -+ /* .resume = ... */ -+ }, -+}; -+ -+ -+/* USB descriptors */ -+ -+#define GS_MANUFACTURER_STR_ID 1 -+#define GS_PRODUCT_STR_ID 2 -+#define GS_SERIAL_STR_ID 3 -+#define GS_CONFIG_STR_ID 4 -+ -+/* static strings, in iso 8859/1 */ -+static char manufacturer[40]; -+static struct usb_string gs_strings[] = { -+ { GS_MANUFACTURER_STR_ID, manufacturer }, -+ { GS_PRODUCT_STR_ID, GS_LONG_NAME }, -+ { GS_SERIAL_STR_ID, "0" }, -+ { GS_CONFIG_STR_ID, "Bulk" }, -+ { } /* end of list */ -+}; -+ -+static struct usb_gadget_strings gs_string_table = { -+ .language = 0x0409, /* en-us */ -+ .strings = gs_strings, -+}; -+ -+static struct usb_device_descriptor gs_device_desc = { -+ .bLength = USB_DT_DEVICE_SIZE, -+ .bDescriptorType = USB_DT_DEVICE, -+ .bcdUSB = __constant_cpu_to_le16(0x0200), -+ .bDeviceClass = USB_CLASS_VENDOR_SPEC, -+ .idVendor = __constant_cpu_to_le16(GS_VENDOR_ID), -+ .idProduct = __constant_cpu_to_le16(GS_PRODUCT_ID), -+ .iManufacturer = GS_MANUFACTURER_STR_ID, -+ .iProduct = GS_PRODUCT_STR_ID, -+ .iSerialNumber = GS_SERIAL_STR_ID, -+ .bNumConfigurations = GS_NUM_CONFIGS, -+}; -+ -+static const struct usb_config_descriptor gs_config_desc = { -+ .bLength = USB_DT_CONFIG_SIZE, -+ .bDescriptorType = USB_DT_CONFIG, -+ /* .wTotalLength set by gs_build_config_desc */ -+ .bNumInterfaces = GS_NUM_INTERFACES, -+ .bConfigurationValue = GS_BULK_CONFIG_ID, -+ .iConfiguration = GS_CONFIG_STR_ID, -+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -+ .bMaxPower = 1, -+}; -+ -+static const struct usb_interface_descriptor gs_interface_desc = { -+ .bLength = USB_DT_INTERFACE_SIZE, -+ .bDescriptorType = USB_DT_INTERFACE, -+ .bNumEndpoints = GS_NUM_ENDPOINTS, -+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, -+ .iInterface = GS_CONFIG_STR_ID, -+}; -+ -+static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+}; -+ -+static struct usb_endpoint_descriptor gs_fullspeed_out_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ .bEndpointAddress = USB_DIR_OUT, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+}; -+ -+static struct usb_endpoint_descriptor gs_highspeed_in_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16(512), -+}; -+ -+static struct usb_endpoint_descriptor gs_highspeed_out_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16(512), -+}; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+static struct usb_qualifier_descriptor gs_qualifier_desc = { -+ .bLength = sizeof(struct usb_qualifier_descriptor), -+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, -+ .bcdUSB = __constant_cpu_to_le16 (0x0200), -+ .bDeviceClass = USB_CLASS_VENDOR_SPEC, -+ /* assumes ep0 uses the same value for both speeds ... */ -+ .bNumConfigurations = GS_NUM_CONFIGS, -+}; -+#endif -+ -+ -+/* Module */ -+ -+MODULE_DESCRIPTION( GS_LONG_NAME ); -+MODULE_AUTHOR( "Al Borchers" ); -+MODULE_LICENSE( "GPL" ); -+ -+#if G_SERIAL_DEBUG -+MODULE_PARM( debug, "i" ); -+MODULE_PARM_DESC( debug, "Enable debugging, 0=off, 1=on" ); -+#endif -+ -+MODULE_PARM( read_q_size, "i" ); -+MODULE_PARM_DESC( read_q_size, "Read request queue size, default=32" ); -+ -+MODULE_PARM( write_q_size, "i" ); -+MODULE_PARM_DESC( write_q_size, "Write request queue size, default=32" ); -+ -+MODULE_PARM( write_buf_size, "i" ); -+MODULE_PARM_DESC( write_buf_size, "Write buffer size, default=8192" ); -+ -+module_init( gs_module_init ); -+module_exit( gs_module_exit ); -+ -+/* -+* gs_module_init -+* -+* Register as a USB gadget driver and a tty driver. -+*/ -+ -+static int __init gs_module_init( void ) -+{ -+ -+ int i,ret; -+ -+ -+ if( (ret=usb_gadget_register_driver( &gs_gadget_driver )) ) { -+ printk( KERN_ERR -+ "gs_module_init: cannot register gadget driver, ret=%d\n", -+ ret ); -+ return( ret ); -+ } -+ -+ /* initial stty settings */ -+ gs_tty_driver.init_termios = tty_std_termios; -+ gs_tty_driver.init_termios.c_cflag -+ = B9600 | CS8 | CREAD | HUPCL | CLOCAL; -+ -+ for( i=0; i<GS_NUM_PORTS; i++ ) -+ sema_init( &gs_open_close_sem[i], 1 ); -+ -+ sema_init( &gs_tmp_buf_sem, 1 ); -+ -+ if( (ret=tty_register_driver( &gs_tty_driver )) ) { -+ usb_gadget_unregister_driver( &gs_gadget_driver ); -+ printk( KERN_ERR -+ "gs_module_init: cannot register tty driver, ret=%d\n", -+ ret ); -+ return( ret ); -+ } -+ -+ printk( KERN_INFO "gs_module_init: %s %s loaded\n", GS_LONG_NAME, -+ GS_VERSION_STR ); -+ -+ return( 0 ); -+ -+} -+ -+ -+/* -+* gs_module_exit -+* -+* Unregister as a tty driver and a USB gadget driver. -+*/ -+ -+static void __exit gs_module_exit( void ) -+{ -+ -+ tty_unregister_driver( &gs_tty_driver ); -+ usb_gadget_unregister_driver( &gs_gadget_driver ); -+ -+ printk( KERN_INFO "gs_module_exit: %s %s unloaded\n", -+ GS_LONG_NAME, GS_VERSION_STR ); -+ -+} -+ -+ -+/* TTY Driver */ -+ -+/* -+ * gs_open -+ */ -+ -+static int gs_open( struct tty_struct *tty, struct file *file ) -+{ -+ -+ int port_num; -+ unsigned long flags; -+ struct gs_port *port; -+ struct gs_dev *dev; -+ struct gs_buf *buf; -+ struct semaphore *sem; -+ -+ -+ port_num = MINOR( tty->device ) - GS_MINOR_START; -+ -+ gs_debug( "gs_open: (%d,%p,%p)\n", port_num, tty, file ); -+ -+ tty->driver_data = NULL; -+ -+ if( port_num < 0 || port_num >= GS_NUM_PORTS ) { -+ printk( KERN_ERR "gs_open: (%d,%p,%p) invalid port number\n", -+ port_num, tty, file ); -+ return( -ENODEV ); -+ } -+ -+ dev = gs_device; -+ -+ if( dev == NULL ) { -+ printk( KERN_ERR "gs_open: (%d,%p,%p) NULL device pointer\n", -+ port_num, tty, file ); -+ return( -ENODEV ); -+ } -+ -+ sem = &gs_open_close_sem[port_num]; -+ if( down_interruptible( sem ) ) { -+ printk( KERN_ERR -+ "gs_open: (%d,%p,%p) interrupted waiting for semaphore\n", -+ port_num, tty, file ); -+ return( -ERESTARTSYS ); -+ } -+ -+ spin_lock_irqsave(&dev->dev_lock, flags ); -+ -+ if( dev->dev_config == GS_NO_CONFIG_ID ) { -+ printk( KERN_ERR -+ "gs_open: (%d,%p,%p) device is not connected\n", -+ port_num, tty, file ); -+ spin_unlock_irqrestore(&dev->dev_lock, flags ); -+ up( sem ); -+ return( -ENODEV ); -+ } -+ -+ port = dev->dev_port[port_num]; -+ -+ if( port == NULL ) { -+ printk( KERN_ERR "gs_open: (%d,%p,%p) NULL port pointer\n", -+ port_num, tty, file ); -+ spin_unlock_irqrestore(&dev->dev_lock, flags ); -+ up( sem ); -+ return( -ENODEV ); -+ } -+ -+ spin_lock(&port->port_lock ); -+ spin_unlock(&dev->dev_lock ); -+ -+ if( port->port_dev == NULL ) { -+ printk( KERN_ERR "gs_open: (%d,%p,%p) port disconnected (1)\n", -+ port_num, tty, file ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return( -EIO ); -+ } -+ -+ if( port->port_open_count > 0 ) { -+ ++port->port_open_count; -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ gs_debug( "gs_open: (%d,%p,%p) already open\n", -+ port_num, tty, file ); -+ up( sem ); -+ return( 0 ); -+ } -+ -+ /* mark port as in use, we can drop port lock and sleep if necessary */ -+ port->port_in_use = 1; -+ -+ /* allocate write buffer on first open */ -+ if( port->port_write_buf == NULL ) { -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ buf = gs_buf_alloc( write_buf_size, GFP_KERNEL ); -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ /* might have been disconnected while asleep, check */ -+ if( port->port_dev == NULL ) { -+ printk( KERN_ERR -+ "gs_open: (%d,%p,%p) port disconnected (2)\n", -+ port_num, tty, file ); -+ port->port_in_use = 0; -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return( -EIO ); -+ } -+ -+ if( (port->port_write_buf=buf) == NULL ) { -+ printk( KERN_ERR "gs_open: (%d,%p,%p) cannot allocate port write buffer\n", -+ port_num, tty, file ); -+ port->port_in_use = 0; -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return( -ENOMEM ); -+ } -+ -+ } -+ -+ /* wait for carrier detect (not implemented) */ -+ -+ /* might have been disconnected while asleep, check */ -+ if( port->port_dev == NULL ) { -+ printk( KERN_ERR "gs_open: (%d,%p,%p) port disconnected (3)\n", -+ port_num, tty, file ); -+ port->port_in_use = 0; -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return( -EIO ); -+ } -+ -+ tty->driver_data = port; -+ port->port_tty = tty; -+ port->port_open_count = 1; -+ port->port_in_use = 0; -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ -+ gs_debug( "gs_open: (%d,%p,%p) completed\n", port_num, tty, file ); -+ -+ return( 0 ); -+ -+} -+ -+ -+/* -+ * gs_close -+ */ -+ -+static void gs_close( struct tty_struct *tty, struct file *file ) -+{ -+ -+ unsigned long flags; -+ struct gs_port *port = tty->driver_data; -+ struct semaphore *sem; -+ -+ -+ if( port == NULL ) { -+ printk( KERN_ERR "gs_close: NULL port pointer\n" ); -+ return; -+ } -+ -+ gs_debug( "gs_close: (%d,%p,%p)\n", port->port_num, tty, file ); -+ -+ sem = &gs_open_close_sem[port->port_num]; -+ down( sem ); -+ -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ if( port->port_open_count == 0 ) { -+ printk( KERN_ERR -+ "gs_close: (%d,%p,%p) port is already closed\n", -+ port->port_num, tty, file ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return; -+ } -+ -+ if( port->port_open_count > 0 ) { -+ --port->port_open_count; -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return; -+ } -+ -+ /* free disconnected port on final close */ -+ if( port->port_dev == NULL ) { -+ kfree( port ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return; -+ } -+ -+ /* mark port as closed but in use, we can drop port lock */ -+ /* and sleep if necessary */ -+ port->port_in_use = 1; -+ port->port_open_count = 0; -+ -+ /* wait for write buffer to drain, or */ -+ /* at most GS_CLOSE_TIMEOUT seconds */ -+ if( gs_buf_data_avail( port->port_write_buf ) > 0 ) { -+ wait_cond_interruptible_timeout( port->port_write_wait, -+ port->port_dev == NULL -+ || gs_buf_data_avail(port->port_write_buf) == 0, -+ &port->port_lock, flags, GS_CLOSE_TIMEOUT * HZ ); -+ } -+ -+ /* free disconnected port on final close */ -+ /* (might have happened during the above sleep) */ -+ if( port->port_dev == NULL ) { -+ kfree( port ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ return; -+ } -+ -+ gs_buf_clear( port->port_write_buf ); -+ -+ tty->driver_data = NULL; -+ port->port_tty = NULL; -+ port->port_in_use = 0; -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ up( sem ); -+ -+ gs_debug( "gs_close: (%d,%p,%p) completed\n", -+ port->port_num, tty, file ); -+ -+} -+ -+ -+/* -+ * gs_write -+ */ -+ -+static int gs_write( struct tty_struct *tty, int from_user, -+ const unsigned char *buf, int count ) -+{ -+ -+ unsigned long flags; -+ struct gs_port *port = tty->driver_data; -+ -+ -+ if( port == NULL ) { -+ printk( KERN_ERR "gs_write: NULL port pointer\n" ); -+ return( -EIO ); -+ } -+ -+ gs_debug( "gs_write: (%d,%p) writing %d bytes\n", port->port_num, tty, -+ count ); -+ -+ if( count == 0 ) -+ return( 0 ); -+ -+ /* copy from user into tmp buffer, get tmp_buf semaphore */ -+ if( from_user ) { -+ if( count > GS_TMP_BUF_SIZE ) -+ count = GS_TMP_BUF_SIZE; -+ down( &gs_tmp_buf_sem ); -+ if( copy_from_user( gs_tmp_buf, buf, count ) != 0 ) { -+ up( &gs_tmp_buf_sem ); -+ printk( KERN_ERR -+ "gs_write: (%d,%p) cannot copy from user space\n", -+ port->port_num, tty ); -+ return( -EFAULT ); -+ } -+ buf = gs_tmp_buf; -+ } -+ -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ if( port->port_dev == NULL ) { -+ printk( KERN_ERR "gs_write: (%d,%p) port is not connected\n", -+ port->port_num, tty ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ if( from_user ) -+ up( &gs_tmp_buf_sem ); -+ return( -EIO ); -+ } -+ -+ if( port->port_open_count == 0 ) { -+ printk( KERN_ERR "gs_write: (%d,%p) port is closed\n", -+ port->port_num, tty ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ if( from_user ) -+ up( &gs_tmp_buf_sem ); -+ return( -EBADF ); -+ } -+ -+ count = gs_buf_put( port->port_write_buf, buf, count ); -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ -+ if( from_user ) -+ up( &gs_tmp_buf_sem ); -+ -+ gs_send( gs_device ); -+ -+ gs_debug( "gs_write: (%d,%p) wrote %d bytes\n", port->port_num, tty, -+ count ); -+ -+ return( count ); -+ -+} -+ -+ -+/* -+ * gs_put_char -+ */ -+ -+static void gs_put_char( struct tty_struct *tty, unsigned char ch ) -+{ -+ -+ unsigned long flags; -+ struct gs_port *port = tty->driver_data; -+ -+ -+ if( port == NULL ) { -+ printk( KERN_ERR "gs_put_char: NULL port pointer\n" ); -+ return; -+ } -+ -+ gs_debug( "gs_put_char: (%d,%p) char=0x%x, called from %p, %p, %p\n", port->port_num, tty, ch, __builtin_return_address(0), __builtin_return_address(1), __builtin_return_address(2) ); -+ -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ if( port->port_dev == NULL ) { -+ printk( KERN_ERR "gs_put_char: (%d,%p) port is not connected\n", -+ port->port_num, tty ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ return; -+ } -+ -+ if( port->port_open_count == 0 ) { -+ printk( KERN_ERR "gs_put_char: (%d,%p) port is closed\n", -+ port->port_num, tty ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ return; -+ } -+ -+ gs_buf_put( port->port_write_buf, &ch, 1 ); -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ -+} -+ -+ -+/* -+ * gs_flush_chars -+ */ -+ -+static void gs_flush_chars( struct tty_struct *tty ) -+{ -+ -+ unsigned long flags; -+ struct gs_port *port = tty->driver_data; -+ -+ -+ if( port == NULL ) { -+ printk( KERN_ERR "gs_flush_chars: NULL port pointer\n" ); -+ return; -+ } -+ -+ gs_debug( "gs_flush_chars: (%d,%p)\n", port->port_num, tty ); -+ -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ if( port->port_dev == NULL ) { -+ printk( KERN_ERR -+ "gs_flush_chars: (%d,%p) port is not connected\n", -+ port->port_num, tty ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ return; -+ } -+ -+ if( port->port_open_count == 0 ) { -+ printk( KERN_ERR "gs_flush_chars: (%d,%p) port is closed\n", -+ port->port_num, tty ); -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ return; -+ } -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ -+ gs_send( gs_device ); -+ -+} -+ -+ -+/* -+ * gs_write_room -+ */ -+ -+static int gs_write_room( struct tty_struct *tty ) -+{ -+ -+ int room = 0; -+ unsigned long flags; -+ struct gs_port *port = tty->driver_data; -+ -+ -+ if( port == NULL ) -+ return( 0 ); -+ -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ if( port->port_dev != NULL && port->port_open_count > 0 -+ && port->port_write_buf != NULL ) -+ room = gs_buf_space_avail( port->port_write_buf ); -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ -+ gs_debug( "gs_write_room: (%d,%p) room=%d\n", -+ port->port_num, tty, room ); -+ -+ return( room ); -+ -+} -+ -+ -+/* -+ * gs_chars_in_buffer -+ */ -+ -+static int gs_chars_in_buffer( struct tty_struct *tty ) -+{ -+ -+ int chars = 0; -+ unsigned long flags; -+ struct gs_port *port = tty->driver_data; -+ -+ -+ if( port == NULL ) -+ return( 0 ); -+ -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ if( port->port_dev != NULL && port->port_open_count > 0 -+ && port->port_write_buf != NULL ) -+ chars = gs_buf_data_avail( port->port_write_buf ); -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ -+ gs_debug( "gs_chars_in_buffer: (%d,%p) chars=%d\n", -+ port->port_num, tty, chars ); -+ -+ return( chars ); -+ -+} -+ -+ -+/* -+ * gs_throttle -+ */ -+ -+static void gs_throttle( struct tty_struct *tty ) -+{ -+ -+} -+ -+ -+/* -+ * gs_unthrottle -+ */ -+ -+static void gs_unthrottle( struct tty_struct *tty ) -+{ -+ -+} -+ -+ -+/* -+ * gs_break -+ */ -+ -+static void gs_break( struct tty_struct *tty, int break_state ) -+{ -+ -+} -+ -+ -+/* -+ * gs_ioctl -+ */ -+ -+static int gs_ioctl( struct tty_struct *tty, struct file *file, -+ unsigned int cmd, unsigned long arg ) -+{ -+ -+ struct gs_port *port = tty->driver_data; -+ -+ -+ if( port == NULL ) { -+ printk( KERN_ERR "gs_ioctl: NULL port pointer\n" ); -+ return( -EIO ); -+ } -+ -+ gs_debug( "gs_ioctl: (%d,%p,%p) cmd=0x%4.4x, arg=%lu\n", -+ port->port_num, tty, file, cmd, arg ); -+ -+ /* handle ioctls */ -+ -+ /* could not handle ioctl */ -+ return( -ENOIOCTLCMD ); -+ -+} -+ -+ -+/* -+ * gs_set_termios -+ */ -+ -+static void gs_set_termios( struct tty_struct *tty, struct termios *old ) -+{ -+ -+} -+ -+ -+/* -+ * gs_read_proc -+ */ -+ -+static int gs_read_proc( char *page, char **start, off_t off, int count, -+ int *eof, void *data ) -+{ -+ -+ return( 0 ); -+ -+} -+ -+ -+/* -+* gs_send -+* -+* This function finds available write requests, calls -+* gs_send_packet to fill these packets with data, and -+* continues until either there are no more write requests -+* available or no more data to send. This function is -+* run whenever data arrives or write requests are available. -+*/ -+ -+static int gs_send( struct gs_dev *dev ) -+{ -+ -+ int ret,len; -+ unsigned long flags; -+ struct usb_ep *ep; -+ struct usb_request *req; -+ struct gs_req_entry *req_entry; -+ -+ -+ if( dev == NULL ) { -+ printk( KERN_ERR "gs_send: NULL device pointer\n" ); -+ return( -ENODEV ); -+ } -+ -+ spin_lock_irqsave(&dev->dev_lock, flags ); -+ -+ ep = dev->dev_in_ep; -+ -+ while( !list_empty( &dev->dev_req_list ) ) { -+ -+ req_entry = list_entry( dev->dev_req_list.next, -+ struct gs_req_entry, re_entry ); -+ -+ req = req_entry->re_req; -+ -+ len = gs_send_packet( dev, req->buf, ep->maxpacket ); -+ -+ if( len > 0 ) { -+gs_debug_level( 3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2) ); -+ list_del( &req_entry->re_entry ); -+ req->length = len; -+ if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) { -+ printk( KERN_ERR -+ "gs_send: cannot queue read request, ret=%d\n", -+ ret ); -+ break; -+ } -+ } else { -+ break; -+ } -+ -+ } -+ -+ spin_unlock_irqrestore(&dev->dev_lock, flags ); -+ -+ return( 0 ); -+ -+} -+ -+ -+/* -+ * gs_send_packet -+ * -+ * If there is data to send, a packet is built in the given -+ * buffer and the size is returned. If there is no data to -+ * send, 0 is returned. If there is any error a negative -+ * error number is returned. -+ * -+ * Called during USB completion routine, on interrupt time. -+ * -+ * We assume that disconnect will not happen until all completion -+ * routines have completed, so we can assume that the dev_port -+ * array does not change during the lifetime of this function. -+ */ -+ -+static int gs_send_packet( struct gs_dev *dev, char *packet, unsigned int size ) -+{ -+ -+ unsigned int len; -+ struct gs_port *port; -+ -+ -+ /* TEMPORARY -- only port 0 is supported right now */ -+ port = dev->dev_port[0]; -+ -+ if( port == NULL ) { -+ printk( KERN_ERR -+ "gs_send_packet: port=%d, NULL port pointer\n", -+ 0 ); -+ return( -EIO ); -+ } -+ -+ spin_lock(&port->port_lock ); -+ -+ len = gs_buf_data_avail( port->port_write_buf ); -+ if( len < size ) -+ size = len; -+ -+ if( size == 0 ) { -+ spin_unlock(&port->port_lock ); -+ return( 0 ); -+ } -+ -+ size = gs_buf_get( port->port_write_buf, packet, size ); -+ -+ wake_up_interruptible( &port->port_tty->write_wait ); -+ -+ spin_unlock(&port->port_lock ); -+ -+ return( size ); -+ -+} -+ -+ -+/* -+ * gs_recv_packet -+ * -+ * Called for each USB packet received. Reads the packet -+ * header and stuffs the data in the appropriate tty buffer. -+ * Returns 0 if successful, or a negative error number. -+ * -+ * Called during USB completion routine, on interrupt time. -+ * -+ * We assume that disconnect will not happen until all completion -+ * routines have completed, so we can assume that the dev_port -+ * array does not change during the lifetime of this function. -+ */ -+ -+static int gs_recv_packet( struct gs_dev *dev, char *packet, unsigned int size ) -+{ -+ -+ unsigned int len; -+ struct gs_port *port; -+ -+ -+ /* TEMPORARY -- only port 0 is supported right now */ -+ port = dev->dev_port[0]; -+ -+ if( port == NULL ) { -+ printk( KERN_ERR "gs_recv_packet: port=%d, NULL port pointer\n", -+ port->port_num ); -+ return( -EIO ); -+ } -+ -+ spin_lock(&port->port_lock ); -+ -+ if( port->port_tty == NULL ) { -+ printk( KERN_ERR "gs_recv_packet: port=%d, NULL tty pointer\n", -+ port->port_num ); -+ spin_unlock(&port->port_lock ); -+ return( -EIO ); -+ } -+ -+ if( port->port_tty->magic != TTY_MAGIC ) { -+ printk( KERN_ERR "gs_recv_packet: port=%d, bad tty magic\n", -+ port->port_num ); -+ spin_unlock(&port->port_lock ); -+ return( -EIO ); -+ } -+ -+ len = (unsigned int)(TTY_FLIPBUF_SIZE - port->port_tty->flip.count); -+ if( len < size ) -+ size = len; -+ -+ if( size > 0 ) { -+ memcpy( port->port_tty->flip.char_buf_ptr, packet, size ); -+ port->port_tty->flip.char_buf_ptr += size; -+ port->port_tty->flip.count += size; -+ tty_flip_buffer_push( port->port_tty ); -+ wake_up_interruptible( &port->port_tty->read_wait ); -+ } -+ -+ spin_unlock(&port->port_lock ); -+ -+ return( 0 ); -+ -+} -+ -+ -+/* -+* gs_read_complete -+*/ -+ -+static void gs_read_complete( struct usb_ep *ep, struct usb_request *req ) -+{ -+ -+ int ret; -+ struct gs_dev *dev = ep->driver_data; -+ -+ -+ if( dev == NULL ) { -+ printk( KERN_ERR "gs_read_complete: NULL device pointer\n" ); -+ return; -+ } -+ -+ switch( req->status ) { -+ -+ case 0: -+ /* normal completion */ -+ gs_recv_packet( dev, req->buf, req->actual ); -+requeue: -+ req->length = ep->maxpacket; -+ if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) { -+ printk( KERN_ERR -+ "gs_read_complete: cannot queue read request, ret=%d\n", -+ ret ); -+ } -+ break; -+ -+ case -ESHUTDOWN: -+ /* disconnect */ -+ gs_debug( "gs_read_complete: shutdown\n" ); -+ gs_free_req( ep, req ); -+ break; -+ -+ default: -+ /* unexpected */ -+ printk( KERN_ERR -+ "gs_read_complete: unexpected status error, status=%d\n", -+ req->status ); -+ goto requeue; -+ break; -+ -+ } -+ -+} -+ -+ -+/* -+* gs_write_complete -+*/ -+ -+static void gs_write_complete( struct usb_ep *ep, struct usb_request *req ) -+{ -+ -+ struct gs_dev *dev = ep->driver_data; -+ struct gs_req_entry *gs_req = req->context; -+ -+ -+ if( dev == NULL ) { -+ printk( KERN_ERR "gs_write_complete: NULL device pointer\n" ); -+ return; -+ } -+ -+ switch( req->status ) { -+ -+ case 0: -+ /* normal completion */ -+requeue: -+ if( gs_req == NULL ) { -+ printk( KERN_ERR -+ "gs_write_complete: NULL request pointer\n" ); -+ return; -+ } -+ -+ spin_lock(&dev->dev_lock ); -+ list_add( &gs_req->re_entry, &dev->dev_req_list ); -+ spin_unlock(&dev->dev_lock ); -+ -+ gs_send( dev ); -+ -+ break; -+ -+ case -ESHUTDOWN: -+ /* disconnect */ -+ gs_debug( "gs_write_complete: shutdown\n" ); -+ gs_free_req( ep, req ); -+ break; -+ -+ default: -+ printk( KERN_ERR -+ "gs_write_complete: unexpected status error, status=%d\n", -+ req->status ); -+ goto requeue; -+ break; -+ -+ } -+ -+} -+ -+ -+/* Gadget Driver */ -+ -+/* -+ * gs_bind -+ * -+ * Called on module load. Allocates and initializes the device -+ * structure and a control request. -+ */ -+static int gs_bind(struct usb_gadget *gadget) -+{ -+ int ret; -+ struct usb_ep *ep; -+ struct gs_dev *dev; -+ -+ usb_ep_autoconfig_reset(gadget); -+ -+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); -+ if (!ep) -+ goto autoconf_fail; -+ EP_IN_NAME = ep->name; -+ ep->driver_data = ep; /* claim the endpoint */ -+ -+ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); -+ if (!ep) -+ goto autoconf_fail; -+ EP_OUT_NAME = ep->name; -+ ep->driver_data = ep; /* claim the endpoint */ -+ -+ /* device specific bcdDevice value in device descriptor */ -+ if (gadget_is_net2280(gadget)) { -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0001); -+ } else if (gadget_is_pxa(gadget)) { -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0002); -+ } else if (gadget_is_sh(gadget)) { -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0003); -+ } else if (gadget_is_sa1100(gadget)) { -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0004); -+ } else if (gadget_is_goku(gadget)) { -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0005); -+ } else if (gadget_is_mq11xx(gadget)) { -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0006); -+ } else if (gadget_is_omap(gadget)) { -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0007); -+ } else { -+ printk(KERN_WARNING "gs_bind: controller '%s' not recognized\n", -+ gadget->name); -+ /* unrecognized, but safe unless bulk is REALLY quirky */ -+ gs_device_desc.bcdDevice = -+ __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); -+ } -+ -+ gs_device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ /* assume ep0 uses the same packet size for both speeds */ -+ gs_qualifier_desc.bMaxPacketSize0 = gs_device_desc.bMaxPacketSize0; -+ /* assume endpoints are dual-speed */ -+ gs_highspeed_in_desc.bEndpointAddress = -+ gs_fullspeed_in_desc.bEndpointAddress; -+ gs_highspeed_out_desc.bEndpointAddress = -+ gs_fullspeed_out_desc.bEndpointAddress; -+#endif /* CONFIG_USB_GADGET_DUALSPEED */ -+ -+ usb_gadget_set_selfpowered(gadget); -+ -+ gs_device = dev = kmalloc(sizeof(struct gs_dev), GFP_KERNEL); -+ if (dev == NULL) -+ return -ENOMEM; -+ -+ snprintf (manufacturer, sizeof(manufacturer), -+ UTS_SYSNAME " " UTS_RELEASE " with %s", gadget->name); -+ -+ memset(dev, 0, sizeof(struct gs_dev)); -+ dev->dev_gadget = gadget; -+ spin_lock_init(&dev->dev_lock); -+ INIT_LIST_HEAD(&dev->dev_req_list); -+ set_gadget_data(gadget, dev); -+ -+ if ((ret = gs_alloc_ports(dev, GFP_KERNEL)) != 0) { -+ printk(KERN_ERR "gs_bind: cannot allocate ports\n"); -+ gs_unbind(gadget); -+ return ret; -+ } -+ -+ /* preallocate control response and buffer */ -+ dev->dev_ctrl_req = gs_alloc_req(gadget->ep0, GS_MAX_DESC_LEN, -+ GFP_KERNEL); -+ if (dev->dev_ctrl_req == NULL) { -+ gs_unbind(gadget); -+ return -ENOMEM; -+ } -+ dev->dev_ctrl_req->complete = gs_setup_complete; -+ -+ gadget->ep0->driver_data = dev; -+ -+ printk(KERN_INFO "gs_bind: %s %s bound\n", -+ GS_LONG_NAME, GS_VERSION_STR); -+ -+ return 0; -+ -+autoconf_fail: -+ printk(KERN_ERR "gs_bind: cannot autoconfigure on %s\n", gadget->name); -+ return -ENODEV; -+} -+ -+ -+/* -+ * gs_unbind -+ * -+ * Called on module unload. Frees the control request and device -+ * structure. -+ */ -+ -+static void gs_unbind( struct usb_gadget *gadget ) -+{ -+ -+ struct gs_dev *dev = get_gadget_data( gadget ); -+ -+ -+ gs_device = NULL; -+ -+ /* read/write requests already freed, only control request remains */ -+ if( dev != NULL ) { -+ if( dev->dev_ctrl_req != NULL ) -+ gs_free_req( gadget->ep0, dev->dev_ctrl_req ); -+ gs_free_ports( dev ); -+ kfree( dev ); -+ set_gadget_data( gadget, NULL ); -+ } -+ -+ printk( KERN_INFO "gs_unbind: %s %s unbound\n", GS_LONG_NAME, -+ GS_VERSION_STR ); -+ -+} -+ -+ -+/* -+ * gs_setup -+ * -+ * Implements all the control endpoint functionality that's not -+ * handled in hardware or the hardware driver. -+ * -+ * Returns the size of the data sent to the host, or a negative -+ * error number. -+ */ -+ -+static int gs_setup( struct usb_gadget *gadget, -+ const struct usb_ctrlrequest *ctrl ) -+{ -+ -+ int ret = -EOPNOTSUPP; -+ unsigned int sv_config; -+ struct gs_dev *dev = get_gadget_data( gadget ); -+ struct usb_request *req = dev->dev_ctrl_req; -+ -+ -+ switch (ctrl->bRequest) { -+ -+ case USB_REQ_GET_DESCRIPTOR: -+ -+ if( ctrl->bRequestType != USB_DIR_IN ) -+ break; -+ -+ switch (ctrl->wValue >> 8) { -+ -+ case USB_DT_DEVICE: -+ ret = min( ctrl->wLength, -+ (u16)sizeof(struct usb_device_descriptor) ); -+ memcpy( req->buf, &gs_device_desc, ret ); -+ break; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ case USB_DT_DEVICE_QUALIFIER: -+ if (!gadget->is_dualspeed) -+ break; -+ ret = min( ctrl->wLength, -+ (u16)sizeof(struct usb_qualifier_descriptor) ); -+ memcpy( req->buf, &gs_qualifier_desc, ret ); -+ break; -+ -+ case USB_DT_OTHER_SPEED_CONFIG: -+#endif /* CONFIG_USB_GADGET_DUALSPEED */ -+ case USB_DT_CONFIG: -+ ret = gs_build_config_desc( req->buf, gadget->speed, -+ ctrl->wValue >> 8, ctrl->wValue & 0xff ); -+ if( ret >= 0 ) -+ ret = min( ctrl->wLength, (u16)ret ); -+ break; -+ -+ case USB_DT_STRING: -+ /* wIndex == language code. */ -+ ret = usb_gadget_get_string( &gs_string_table, -+ ctrl->wValue & 0xff, req->buf ); -+ if( ret >= 0 ) -+ ret = min( ctrl->wLength, (u16)ret ); -+ break; -+ } -+ break; -+ -+ case USB_REQ_SET_CONFIGURATION: -+ if( ctrl->bRequestType != 0 ) -+ break; -+ spin_lock( &dev->dev_lock ); -+ ret = gs_set_config( dev, ctrl->wValue ); -+ spin_unlock( &dev->dev_lock ); -+ break; -+ -+ case USB_REQ_GET_CONFIGURATION: -+ if( ctrl->bRequestType != USB_DIR_IN ) -+ break; -+ *(u8 *)req->buf = dev->dev_config; -+ ret = min( ctrl->wLength, (u16)1 ); -+ break; -+ -+ case USB_REQ_SET_INTERFACE: -+ if( ctrl->bRequestType != USB_RECIP_INTERFACE ) -+ break; -+ spin_lock( &dev->dev_lock ); -+ if( dev->dev_config == GS_BULK_CONFIG_ID -+ && ctrl->wIndex == GS_INTERFACE_ID -+ && ctrl->wValue == GS_ALT_INTERFACE_ID ) { -+ sv_config = dev->dev_config; -+ /* since there is only one interface, setting the */ -+ /* interface is equivalent to setting the config */ -+ gs_reset_config( dev ); -+ gs_set_config( dev, sv_config ); -+ ret = 0; -+ } -+ spin_unlock( &dev->dev_lock ); -+ break; -+ -+ case USB_REQ_GET_INTERFACE: -+ if( ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE) ) -+ break; -+ if( dev->dev_config == GS_NO_CONFIG_ID ) -+ break; -+ if( ctrl->wIndex != GS_INTERFACE_ID ) { -+ ret = -EDOM; -+ break; -+ } -+ *(u8 *)req->buf = GS_ALT_INTERFACE_ID; -+ ret = min( ctrl->wLength, (u16)1 ); -+ break; -+ -+ default: -+ printk( KERN_ERR "gs_setup: unknown request, type=%02x, request=%02x, value=%04x, index=%04x, length=%d\n", -+ ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, -+ ctrl->wIndex, ctrl->wLength ); -+ break; -+ -+ } -+ -+ /* respond with data transfer before status phase? */ -+ if( ret >= 0 ) { -+ req->length = ret; -+ ret = usb_ep_queue( gadget->ep0, req, GFP_ATOMIC ); -+ if( ret < 0 ) { -+ printk( KERN_ERR -+ "gs_setup: cannot queue response, ret=%d\n", -+ ret ); -+ req->status = 0; -+ gs_setup_complete( gadget->ep0, req ); -+ } -+ } -+ -+ /* device either stalls (ret < 0) or reports success */ -+ return( ret ); -+ -+} -+ -+ -+/* -+ * gs_setup_complete -+ */ -+ -+static void gs_setup_complete( struct usb_ep *ep, struct usb_request *req ) -+{ -+ if( req->status || req->actual != req->length ) { -+ printk( KERN_ERR "gs_setup_complete: status error, status=%d, actual=%d, length=%d\n", -+ req->status, req->actual, req->length ); -+ } -+} -+ -+ -+/* -+ * gs_disconnect -+ * -+ * Called when the device is disconnected. Frees the closed -+ * ports and disconnects open ports. Open ports will be freed -+ * on close. Then reallocates the ports for the next connection. -+ */ -+ -+static void gs_disconnect( struct usb_gadget *gadget ) -+{ -+ -+ unsigned long flags; -+ struct gs_dev *dev = get_gadget_data( gadget ); -+ -+ -+ spin_lock_irqsave( &dev->dev_lock, flags ); -+ -+ gs_reset_config( dev ); -+ -+ /* free closed ports and disconnect open ports */ -+ /* (open ports will be freed when closed) */ -+ gs_free_ports( dev ); -+ -+ /* re-allocate ports for the next connection */ -+ if( gs_alloc_ports( dev, GFP_ATOMIC ) != 0 ) -+ printk( KERN_ERR "gs_disconnect: cannot re-allocate ports\n" ); -+ -+ spin_unlock_irqrestore( &dev->dev_lock, flags ); -+ -+ printk( KERN_INFO "gs_disconnect: %s disconnected\n", GS_LONG_NAME ); -+ -+} -+ -+ -+/* -+ * gs_set_config -+ * -+ * Configures the device by enabling device specific -+ * optimizations, setting up the endpoints, allocating -+ * read and write requests and queuing read requests. -+ * -+ * The device lock must be held when calling this function. -+ */ -+ -+static int gs_set_config( struct gs_dev *dev, unsigned config ) -+{ -+ -+ int i; -+ int ret = 0; -+ struct usb_gadget *gadget = dev->dev_gadget; -+ struct usb_ep *ep; -+ struct usb_request *req; -+ struct gs_req_entry *req_entry; -+ -+ -+ if( dev == NULL ) { -+ printk( KERN_ERR "gs_set_config: NULL device pointer\n" ); -+ return( 0 ); -+ } -+ -+ if( config == dev->dev_config ) -+ return( 0 ); -+ -+ gs_reset_config( dev ); -+ -+ if( config == GS_NO_CONFIG_ID ) -+ return( 0 ); -+ -+ if( config != GS_BULK_CONFIG_ID ) -+ return( -EINVAL ); -+ -+ /* device specific optimizations */ -+ if (gadget_is_net2280(gadget)) -+ net2280_set_fifo_mode(gadget, 1); -+ -+ gadget_for_each_ep( ep, gadget ) { -+ -+ if( strcmp( ep->name, EP_IN_NAME ) == 0 ) { -+ ret = usb_ep_enable( ep, -+ gadget->speed == USB_SPEED_HIGH ? -+ &gs_highspeed_in_desc : &gs_fullspeed_in_desc ); -+ if( ret == 0 ) { -+ ep->driver_data = dev; -+ dev->dev_in_ep = ep; -+ } else { -+ printk( KERN_ERR "gs_set_config: cannot enable in endpoint %s, ret=%d\n", -+ ep->name, ret ); -+ gs_reset_config( dev ); -+ return( ret ); -+ } -+ } -+ -+ else if( strcmp( ep->name, EP_OUT_NAME ) == 0 ) { -+ ret = usb_ep_enable( ep, -+ gadget->speed == USB_SPEED_HIGH ? -+ &gs_highspeed_out_desc : -+ &gs_fullspeed_out_desc ); -+ if( ret == 0 ) { -+ ep->driver_data = dev; -+ dev->dev_out_ep = ep; -+ } else { -+ printk( KERN_ERR "gs_set_config: cannot enable out endpoint %s, ret=%d\n", -+ ep->name, ret ); -+ gs_reset_config( dev ); -+ return( ret ); -+ } -+ } -+ -+ } -+ -+ if( dev->dev_in_ep == NULL || dev->dev_out_ep == NULL ) { -+ gs_reset_config( dev ); -+ printk( KERN_ERR "gs_set_config: cannot find endpoints\n" ); -+ return( -ENODEV ); -+ } -+ -+ /* allocate and queue read requests */ -+ ep = dev->dev_out_ep; -+ for( i=0; i<read_q_size && ret == 0; i++ ) { -+ if( (req=gs_alloc_req( ep, ep->maxpacket, GFP_ATOMIC )) ) { -+ req->complete = gs_read_complete; -+ if( (ret=usb_ep_queue( ep, req, GFP_ATOMIC )) ) { -+ printk( KERN_ERR "gs_set_config: cannot queue read request, ret=%d\n", -+ ret ); -+ } -+ } else { -+ gs_reset_config( dev ); -+ printk( KERN_ERR -+ "gs_set_config: cannot allocate read requests\n" ); -+ return( -ENOMEM ); -+ } -+ } -+ -+ /* allocate write requests, and put on free list */ -+ ep = dev->dev_in_ep; -+ for( i=0; i<write_q_size; i++ ) { -+ if( (req_entry=gs_alloc_req_entry( ep, ep->maxpacket, -+ GFP_ATOMIC )) ) { -+ req_entry->re_req->complete = gs_write_complete; -+ list_add( &req_entry->re_entry, &dev->dev_req_list ); -+ } else { -+ gs_reset_config( dev ); -+ printk( KERN_ERR -+ "gs_set_config: cannot allocate write requests\n" ); -+ return( -ENOMEM ); -+ } -+ } -+ -+ dev->dev_config = config; -+ -+ printk( KERN_INFO "gs_set_config: %s configured for %s speed\n", -+ GS_LONG_NAME, -+ gadget->speed == USB_SPEED_HIGH ? "high" : "full" ); -+ -+ return( 0 ); -+ -+} -+ -+ -+/* -+ * gs_reset_config -+ * -+ * Mark the device as not configured, disable all endpoints, -+ * which forces completion of pending I/O and frees queued -+ * requests, and free the remaining write requests on the -+ * free list. -+ * -+ * The device lock must be held when calling this function. -+ */ -+ -+static void gs_reset_config( struct gs_dev *dev ) -+{ -+ -+ struct gs_req_entry *req_entry; -+ -+ -+ if( dev == NULL ) { -+ printk( KERN_ERR "gs_reset_config: NULL device pointer\n" ); -+ return; -+ } -+ -+ if( dev->dev_config == GS_NO_CONFIG_ID ) -+ return; -+ -+ dev->dev_config = GS_NO_CONFIG_ID; -+ -+ /* free write requests on the free list */ -+ while( !list_empty( &dev->dev_req_list ) ) { -+ req_entry = list_entry( dev->dev_req_list.next, -+ struct gs_req_entry, re_entry ); -+ list_del( &req_entry->re_entry ); -+ gs_free_req_entry( dev->dev_in_ep, req_entry ); -+ } -+ -+ /* disable endpoints, forcing completion of pending i/o; */ -+ /* completion handlers free their requests in this case */ -+ if( dev->dev_in_ep ) { -+ usb_ep_disable( dev->dev_in_ep ); -+ dev->dev_in_ep = NULL; -+ } -+ if( dev->dev_out_ep ) { -+ usb_ep_disable( dev->dev_out_ep ); -+ dev->dev_out_ep = NULL; -+ } -+ -+} -+ -+ -+/* -+ * gs_build_config_desc -+ * -+ * Builds a config descriptor in the given buffer and returns the -+ * length, or a negative error number. -+ */ -+ -+static int gs_build_config_desc( u8 *buf, enum usb_device_speed speed, -+ u8 type, unsigned int index ) -+{ -+ -+ int high_speed; -+ int len = USB_DT_CONFIG_SIZE + USB_DT_INTERFACE_SIZE -+ + GS_NUM_ENDPOINTS * USB_DT_ENDPOINT_SIZE; -+ -+ -+ /* only one config */ -+ if( index != 0 ) -+ return( -EINVAL ); -+ -+ memcpy( buf, &gs_config_desc, USB_DT_CONFIG_SIZE ); -+ ((struct usb_config_descriptor *)buf)->bDescriptorType = type; -+ ((struct usb_config_descriptor *)buf)->wTotalLength = -+ __constant_cpu_to_le16( len ); -+ buf += USB_DT_CONFIG_SIZE; -+ -+ memcpy( buf, &gs_interface_desc, USB_DT_INTERFACE_SIZE ); -+ buf += USB_DT_INTERFACE_SIZE; -+ -+ /* other speed switches high and full speed */ -+ high_speed = (speed == USB_SPEED_HIGH); -+ if( type == USB_DT_OTHER_SPEED_CONFIG ) -+ high_speed = !high_speed; -+ -+ memcpy( buf, -+ high_speed ? &gs_highspeed_in_desc : &gs_fullspeed_in_desc, -+ USB_DT_ENDPOINT_SIZE ); -+ buf += USB_DT_ENDPOINT_SIZE; -+ memcpy( buf, -+ high_speed ? &gs_highspeed_out_desc : &gs_fullspeed_out_desc, -+ USB_DT_ENDPOINT_SIZE ); -+ -+ return( len ); -+ -+} -+ -+ -+/* -+ * gs_alloc_req -+ * -+ * Allocate a usb_request and its buffer. Returns a pointer to the -+ * usb_request or NULL if there is an error. -+ */ -+ -+static struct usb_request *gs_alloc_req( struct usb_ep *ep, unsigned int len, -+ int kmalloc_flags ) -+{ -+ -+ struct usb_request *req; -+ -+ -+ if( ep == NULL ) -+ return( NULL ); -+ -+ req = usb_ep_alloc_request( ep, kmalloc_flags ); -+ -+ if( req != NULL ) { -+ req->length = len; -+ req->buf = usb_ep_alloc_buffer( ep, len, &req->dma, -+ kmalloc_flags ); -+ if( req->buf == NULL ) { -+ usb_ep_free_request( ep, req ); -+ return( NULL ); -+ } -+ } -+ -+ return( req ); -+ -+} -+ -+ -+/* -+ * gs_free_req -+ * -+ * Free a usb_request and its buffer. -+ */ -+ -+static void gs_free_req( struct usb_ep *ep, struct usb_request *req ) -+{ -+ if( ep != NULL && req != NULL ) { -+ if( req->buf != NULL ) -+ usb_ep_free_buffer( ep, req->buf, req->dma, -+ req->length ); -+ usb_ep_free_request( ep, req ); -+ } -+} -+ -+ -+/* -+ * gs_alloc_req_entry -+ * -+ * Allocates a request and its buffer, using the given -+ * endpoint, buffer len, and kmalloc flags. -+ */ -+ -+static struct gs_req_entry *gs_alloc_req_entry( struct usb_ep *ep, -+ unsigned len, int kmalloc_flags ) -+{ -+ -+ struct gs_req_entry *req; -+ -+ -+ req = kmalloc( sizeof(struct gs_req_entry), kmalloc_flags ); -+ if( req == NULL ) -+ return( NULL ); -+ -+ req->re_req = gs_alloc_req( ep, len, kmalloc_flags ); -+ if( req->re_req == NULL ) { -+ kfree( req ); -+ return( NULL ); -+ } -+ -+ req->re_req->context = req; -+ -+ return( req ); -+ -+} -+ -+ -+/* -+ * gs_free_req_entry -+ * -+ * Frees a request and its buffer. -+ */ -+ -+static void gs_free_req_entry( struct usb_ep *ep, struct gs_req_entry *req ) -+{ -+ if( ep != NULL && req != NULL ) { -+ if( req->re_req != NULL ) -+ gs_free_req( ep, req->re_req ); -+ kfree( req ); -+ } -+} -+ -+ -+/* -+ * gs_alloc_ports -+ * -+ * Allocate all ports and set the gs_dev struct to point to them. -+ * Return 0 if successful, or a negative error number. -+ * -+ * The device lock is normally held when calling this function. -+ */ -+ -+static int gs_alloc_ports( struct gs_dev *dev, int kmalloc_flags ) -+{ -+ -+ int i; -+ struct gs_port *port; -+ -+ -+ if( dev == NULL ) -+ return( -EIO ); -+ -+ for( i=0; i<GS_NUM_PORTS; i++ ) { -+ -+ if( (port=(struct gs_port *)kmalloc( sizeof(struct gs_port), -+ kmalloc_flags )) == NULL ) -+ return( -ENOMEM ); -+ -+ memset( port, 0, sizeof( struct gs_port ) ); -+ port->port_dev = dev; -+ port->port_num = i; -+ spin_lock_init( &port->port_lock ); -+ init_waitqueue_head( &port->port_write_wait ); -+ -+ dev->dev_port[i] = port; -+ -+ } -+ -+ return( 0 ); -+ -+} -+ -+ -+/* -+ * gs_free_ports -+ * -+ * Free all closed ports. Open ports are disconnected by -+ * freeing their write buffers, setting their device pointers -+ * and the pointers to them in the device to NULL. These -+ * ports will be freed when closed. -+ * -+ * The device lock is normally held when calling this function. -+ */ -+ -+static void gs_free_ports( struct gs_dev *dev ) -+{ -+ -+ int i; -+ unsigned long flags; -+ struct gs_port *port; -+ -+ -+ if( dev == NULL ) -+ return; -+ -+ for( i=0; i<GS_NUM_PORTS; i++ ) { -+ -+ if( (port=dev->dev_port[i]) != NULL ) { -+ -+ dev->dev_port[i] = NULL; -+ -+ spin_lock_irqsave(&port->port_lock, flags ); -+ -+ if( port->port_write_buf != NULL ) { -+ gs_buf_free( port->port_write_buf ); -+ port->port_write_buf = NULL; -+ } -+ -+ if( port->port_open_count > 0 || port->port_in_use ) { -+ port->port_dev = NULL; -+ wake_up_interruptible( &port->port_write_wait ); -+ wake_up_interruptible( &port->port_tty->read_wait ); -+ wake_up_interruptible( &port->port_tty->write_wait ); -+ } else { -+ kfree( port ); -+ } -+ -+ spin_unlock_irqrestore(&port->port_lock, flags ); -+ -+ } -+ -+ } -+ -+} -+ -+ -+/* Circular Buffer */ -+ -+/* -+ * gs_buf_alloc -+ * -+ * Allocate a circular buffer and all associated memory. -+ */ -+ -+static struct gs_buf *gs_buf_alloc( unsigned int size, int kmalloc_flags ) -+{ -+ -+ struct gs_buf *gb; -+ -+ -+ if( size == 0 ) -+ return( NULL ); -+ -+ gb = (struct gs_buf *)kmalloc( sizeof(struct gs_buf), kmalloc_flags ); -+ if( gb == NULL ) -+ return( NULL ); -+ -+ gb->buf_buf = kmalloc( size, kmalloc_flags ); -+ if( gb->buf_buf == NULL ) { -+ kfree( gb ); -+ return( NULL ); -+ } -+ -+ gb->buf_size = size; -+ gb->buf_get = gb->buf_put = gb->buf_buf; -+ -+ return( gb ); -+ -+} -+ -+ -+/* -+ * gs_buf_free -+ * -+ * Free the buffer and all associated memory. -+ */ -+ -+void gs_buf_free( struct gs_buf *gb ) -+{ -+ if( gb != NULL ) { -+ if( gb->buf_buf != NULL ) -+ kfree( gb->buf_buf ); -+ kfree( gb ); -+ } -+} -+ -+ -+/* -+ * gs_buf_clear -+ * -+ * Clear out all data in the circular buffer. -+ */ -+ -+void gs_buf_clear( struct gs_buf *gb ) -+{ -+ if( gb != NULL ) -+ gb->buf_get = gb->buf_put; -+ /* equivalent to a get of all data available */ -+} -+ -+ -+/* -+ * gs_buf_data_avail -+ * -+ * Return the number of bytes of data available in the circular -+ * buffer. -+ */ -+ -+unsigned int gs_buf_data_avail( struct gs_buf *gb ) -+{ -+ if( gb != NULL ) -+ return( (gb->buf_size + gb->buf_put - gb->buf_get) -+ % gb->buf_size ); -+ else -+ return( 0 ); -+} -+ -+ -+/* -+ * gs_buf_space_avail -+ * -+ * Return the number of bytes of space available in the circular -+ * buffer. -+ */ -+ -+unsigned int gs_buf_space_avail( struct gs_buf *gb ) -+{ -+ if( gb != NULL ) -+ return( (gb->buf_size + gb->buf_get - gb->buf_put - 1) -+ % gb->buf_size ); -+ else -+ return( 0 ); -+} -+ -+ -+/* -+ * gs_buf_put -+ * -+ * Copy data data from a user buffer and put it into the circular buffer. -+ * Restrict to the amount of space available. -+ * -+ * Return the number of bytes copied. -+ */ -+ -+unsigned int gs_buf_put( struct gs_buf *gb, const char *buf, -+ unsigned int count ) -+{ -+ -+ unsigned int len; -+ -+ -+ if( gb == NULL ) -+ return( 0 ); -+ -+ len = gs_buf_space_avail( gb ); -+ if( count > len ) -+ count = len; -+ -+ if( count == 0 ) -+ return( 0 ); -+ -+ len = gb->buf_buf + gb->buf_size - gb->buf_put; -+ if( count > len ) { -+ memcpy( gb->buf_put, buf, len ); -+ memcpy( gb->buf_buf, buf+len, count - len ); -+ gb->buf_put = gb->buf_buf + count - len; -+ } else { -+ memcpy( gb->buf_put, buf, count ); -+ if( count < len ) -+ gb->buf_put += count; -+ else /* count == len */ -+ gb->buf_put = gb->buf_buf; -+ } -+ -+ return( count ); -+ -+} -+ -+ -+/* -+ * gs_buf_get -+ * -+ * Get data from the circular buffer and copy to the given buffer. -+ * Restrict to the amount of data available. -+ * -+ * Return the number of bytes copied. -+ */ -+ -+unsigned int gs_buf_get( struct gs_buf *gb, char *buf, unsigned int count ) -+{ -+ -+ unsigned int len; -+ -+ -+ if( gb == NULL ) -+ return( 0 ); -+ -+ len = gs_buf_data_avail( gb ); -+ if( count > len ) -+ count = len; -+ -+ if( count == 0 ) -+ return( 0 ); -+ -+ len = gb->buf_buf + gb->buf_size - gb->buf_get; -+ if( count > len ) { -+ memcpy( buf, gb->buf_get, len ); -+ memcpy( buf+len, gb->buf_buf, count - len ); -+ gb->buf_get = gb->buf_buf + count - len; -+ } else { -+ memcpy( buf, gb->buf_get, count ); -+ if( count < len ) -+ gb->buf_get += count; -+ else /* count == len */ -+ gb->buf_get = gb->buf_buf; -+ } -+ -+ return( count ); -+ -+} -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/inode.c kernel/drivers/usb/gadget/inode.c ---- /tmp/kernel/drivers/usb/gadget/inode.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/inode.c 2005-04-22 17:53:19.456535934 +0200 -@@ -0,0 +1,1807 @@ -+/* -+ * inode.c -- user mode filesystem api for usb gadget controllers -+ * -+ * Copyright (C) 2003 David Brownell -+ * Copyright (C) 2003 Agilent Technologies -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+ -+#define DEBUG 1 /* data to help fault diagnosis */ -+// #define VERBOSE /* extra debug messages (success too) */ -+ -+#include <linux/init.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/fs.h> -+#include <linux/pagemap.h> -+#include <linux/uts.h> -+#include <linux/version.h> -+#include <linux/wait.h> -+#include <linux/compiler.h> -+#include <asm/uaccess.h> -+#include <linux/slab.h> -+ -+#ifndef BUG_ON -+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) -+#endif -+ -+#include <linux/usb_gadgetfs.h> -+#include <linux/usb_gadget.h> -+ -+ -+/* -+ * The gadgetfs API maps each endpoint to a file descriptor so that you -+ * can use standard synchronous read/write calls for I/O. There's some -+ * O_NONBLOCK and O_ASYNC/FASYNC style i/o support. Example usermode -+ * drivers show how this works in practice. -+ * -+ * Key parts that must be USB-specific are protocols defining how the -+ * read/write operations relate to the hardware state machines. There -+ * are two types of files. One type is for the device, implementing ep0. -+ * The other type is for each IN or OUT endpoint. In both cases, the -+ * user mode driver must configure the hardware before using it. -+ * -+ * - First, dev_config() is called when /dev/gadget/$CHIP is configured -+ * (by writing configuration and device descriptors). Afterwards it -+ * may serve as a source of device events, used to handle all control -+ * requests other than basic enumeration. -+ * -+ * - Then either immediately, or after a SET_CONFIGURATION control request, -+ * ep_config() is called when each /dev/gadget/ep* file is configured -+ * (by writing endpoint descriptors). Afterwards these files are used -+ * to write() IN data or to read() OUT data. To halt the endpoint, a -+ * "wrong direction" request is issued (like reading an IN endpoint). -+ * -+ * Unlike "usbfs" the only ioctl()s are for things that are rare, and maybe -+ * not possible on all hardware. For example, precise fault handling with -+ * respect to data left in endpoint fifos after aborted operations; or -+ * selective clearing of endpoint halts, to implement SET_INTERFACE. -+ */ -+ -+#define DRIVER_DESC "USB Gadget filesystem" -+#define DRIVER_VERSION "20 Aug 2003" -+ -+static const char driver_desc [] = DRIVER_DESC; -+static const char shortname [] = "gadgetfs"; -+ -+MODULE_DESCRIPTION (DRIVER_DESC); -+MODULE_AUTHOR ("David Brownell"); -+MODULE_LICENSE ("GPL"); -+ -+ -+/*----------------------------------------------------------------------*/ -+ -+#define GADGETFS_MAGIC 0xaee71ee7 -+#define DMA_ADDR_INVALID (~(dma_addr_t)0) -+ -+/* /dev/gadget/$CHIP represents ep0 and the whole device */ -+enum ep0_state { -+ /* DISBLED is the initial state. -+ */ -+ STATE_DEV_DISABLED = 0, -+ -+ /* Only one open() of /dev/gadget/$CHIP; only one file tracks -+ * ep0/device i/o modes and binding to the controller. Driver -+ * must always write descriptors to initialize the device, then -+ * the device becomes UNCONNECTED until enumeration. -+ */ -+ STATE_OPENED, -+ -+ /* From then on, ep0 fd is in either of two basic modes: -+ * - (UN)CONNECTED: read usb_gadgetfs_event(s) from it -+ * - SETUP: read/write will transfer control data and succeed; -+ * or if "wrong direction", performs protocol stall -+ */ -+ STATE_UNCONNECTED, -+ STATE_CONNECTED, -+ STATE_SETUP, -+ -+ /* UNBOUND means the driver closed ep0, so the device won't be -+ * accessible again (DEV_DISABLED) until all fds are closed. -+ */ -+ STATE_DEV_UNBOUND, -+}; -+ -+/* enough for the whole queue: most events invalidate others */ -+#define N_EVENT 5 -+ -+struct dev_data { -+ spinlock_t lock; -+ atomic_t count; -+ enum ep0_state state; -+ struct usb_gadgetfs_event event [N_EVENT]; -+ unsigned ev_next; -+ struct fasync_struct *fasync; -+ u8 current_config; -+ -+ /* drivers reading ep0 MUST handle control requests (SETUP) -+ * reported that way; else the host will time out. -+ */ -+ unsigned usermode_setup : 1, -+ setup_in : 1, -+ setup_can_stall : 1, -+ setup_out_ready : 1, -+ setup_out_error : 1, -+ setup_abort : 1; -+ -+ /* the rest is basically write-once */ -+ struct usb_config_descriptor *config, *hs_config; -+ struct usb_device_descriptor *dev; -+ struct usb_request *req; -+ struct usb_gadget *gadget; -+ struct list_head epfiles; -+ void *buf; -+ wait_queue_head_t wait; -+ struct super_block *sb; -+ struct dentry *dentry; -+ -+ /* except this scratch i/o buffer for ep0 */ -+ u8 rbuf [256]; -+}; -+ -+static inline void get_dev (struct dev_data *data) -+{ -+ atomic_inc (&data->count); -+} -+ -+static void put_dev (struct dev_data *data) -+{ -+ if (likely (!atomic_dec_and_test (&data->count))) -+ return; -+ /* needs no more cleanup */ -+ BUG_ON (waitqueue_active (&data->wait)); -+ kfree (data); -+} -+ -+static struct dev_data *dev_new (void) -+{ -+ struct dev_data *dev; -+ -+ dev = kmalloc (sizeof *dev, GFP_KERNEL); -+ if (!dev) -+ return 0; -+ memset (dev, 0, sizeof *dev); -+ dev->state = STATE_DEV_DISABLED; -+ atomic_set (&dev->count, 1); -+ spin_lock_init (&dev->lock); -+ INIT_LIST_HEAD (&dev->epfiles); -+ init_waitqueue_head (&dev->wait); -+ return dev; -+} -+ -+/*----------------------------------------------------------------------*/ -+ -+/* other /dev/gadget/$ENDPOINT files represent endpoints */ -+enum ep_state { -+ STATE_EP_DISABLED = 0, -+ STATE_EP_READY, -+ STATE_EP_DEFER_ENABLE, -+ STATE_EP_ENABLED, -+ STATE_EP_UNBOUND, -+}; -+ -+struct ep_data { -+ struct semaphore lock; -+ enum ep_state state; -+ atomic_t count; -+ struct dev_data *dev; -+ /* must hold dev->lock before accessing ep or req */ -+ struct usb_ep *ep; -+ struct usb_request *req; -+ ssize_t status; -+ char name [16]; -+ struct usb_endpoint_descriptor desc, hs_desc; -+ struct list_head epfiles; -+ wait_queue_head_t wait; -+ struct dentry *dentry; -+ struct inode *inode; -+}; -+ -+static inline void get_ep (struct ep_data *data) -+{ -+ atomic_inc (&data->count); -+} -+ -+static void put_ep (struct ep_data *data) -+{ -+ if (likely (!atomic_dec_and_test (&data->count))) -+ return; -+ put_dev (data->dev); -+ /* needs no more cleanup */ -+ BUG_ON (!list_empty (&data->epfiles)); -+ BUG_ON (waitqueue_active (&data->wait)); -+ BUG_ON (down_trylock (&data->lock) != 0); -+ kfree (data); -+} -+ -+/*----------------------------------------------------------------------*/ -+ -+/* most "how to use the hardware" policy choices are in userspace: -+ * mapping endpoint roles the driver needs to the capabilities that -+ * the usb controller exposes. -+ */ -+ -+ // FIXME the 2.6 version just probes the controller -+ // driver to find out the chip name; we should too. -+ -+#ifdef CONFIG_USB_GADGET_NET2280 -+#define CHIP "net2280" -+#define HIGHSPEED -+#endif -+ -+#ifdef CONFIG_USB_GADGET_PXA2XX -+#define CHIP "pxa2xx_udc" -+/* earlier hardware doesn't have UDCCFR, races set_{config,interface} */ -+#warning works best with pxa255 or newer -+#endif -+ -+#ifdef CONFIG_USB_GADGET_GOKU -+#define CHIP "goku_udc" -+#endif -+ -+#ifdef CONFIG_USB_GADGET_SA1100 -+#define CHIP "sa1100" -+#endif -+ -+#ifdef CONFIG_USB_GADGET_SUPERH -+#define CHIP "superh_udc" -+#endif -+ -+#ifdef CONFIG_USB_GADGET_N9604 -+#define CHIP "n9604_udc" -+#endif -+ -+ -+/*----------------------------------------------------------------------*/ -+ -+/* NOTE: don't use dev_printk calls before binding to the gadget -+ * at the end of ep0 configuration, or after unbind. -+ */ -+ -+/* too wordy: dev_printk(level , &(d)->gadget->dev , fmt , ## args) */ -+#define xprintk(d,level,fmt,args...) \ -+ printk(level "%s: " fmt , shortname , ## args) -+ -+#ifdef DEBUG -+#define DBG(dev,fmt,args...) \ -+ xprintk(dev , KERN_DEBUG , fmt , ## args) -+#else -+#define DBG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+#ifdef VERBOSE -+#define VDEBUG DBG -+#else -+#define VDEBUG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+#define ERROR(dev,fmt,args...) \ -+ xprintk(dev , KERN_ERR , fmt , ## args) -+#define WARN(dev,fmt,args...) \ -+ xprintk(dev , KERN_WARNING , fmt , ## args) -+#define INFO(dev,fmt,args...) \ -+ xprintk(dev , KERN_INFO , fmt , ## args) -+ -+ -+/*----------------------------------------------------------------------*/ -+ -+/* SYNCHRONOUS ENDPOINT OPERATIONS (bulk/intr/iso) -+ * -+ * After opening, configure non-control endpoints. Then use normal -+ * stream read() and write() requests; and maybe ioctl() to get more -+ * precise FIFO status when recovering from cancelation. -+ */ -+ -+static void epio_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct ep_data *epdata = ep->driver_data; -+ -+ if (!req->context) -+ return; -+ if (req->status) -+ epdata->status = req->status; -+ else -+ epdata->status = req->actual; -+ complete ((struct completion *)req->context); -+} -+ -+/* tasklock endpoint, returning when it's connected. -+ * still need dev->lock to use epdata->ep. -+ */ -+static int -+get_ready_ep (unsigned f_flags, struct ep_data *epdata) -+{ -+ int val; -+ -+ if (f_flags & O_NONBLOCK) { -+ if (down_trylock (&epdata->lock) != 0) -+ goto nonblock; -+ if (epdata->state != STATE_EP_ENABLED) { -+ up (&epdata->lock); -+nonblock: -+ val = -EAGAIN; -+ } else -+ val = 0; -+ return val; -+ } -+ -+ if ((val = down_interruptible (&epdata->lock)) < 0) -+ return val; -+newstate: -+ switch (epdata->state) { -+ case STATE_EP_ENABLED: -+ break; -+ case STATE_EP_DEFER_ENABLE: -+ DBG (epdata->dev, "%s wait for host\n", epdata->name); -+ if ((val = wait_event_interruptible (epdata->wait, -+ epdata->state != STATE_EP_DEFER_ENABLE -+ || epdata->dev->state == STATE_DEV_UNBOUND -+ )) < 0) -+ goto fail; -+ goto newstate; -+ // case STATE_EP_DISABLED: /* "can't happen" */ -+ // case STATE_EP_READY: /* "can't happen" */ -+ default: /* error! */ -+ pr_debug ("%s: ep %p not available, state %d\n", -+ shortname, epdata, epdata->state); -+ // FALLTHROUGH -+ case STATE_EP_UNBOUND: /* clean disconnect */ -+ val = -ENODEV; -+fail: -+ up (&epdata->lock); -+ } -+ return val; -+} -+ -+static ssize_t -+ep_io (struct ep_data *epdata, void *buf, unsigned len) -+{ -+ DECLARE_COMPLETION (done); -+ int value; -+ -+ spin_lock_irq (&epdata->dev->lock); -+ if (likely (epdata->ep != NULL)) { -+ struct usb_request *req = epdata->req; -+ -+ req->context = &done; -+ req->complete = epio_complete; -+ req->buf = buf; -+ req->length = len; -+ value = usb_ep_queue (epdata->ep, req, GFP_ATOMIC); -+ } else -+ value = -ENODEV; -+ spin_unlock_irq (&epdata->dev->lock); -+ -+ if (likely (value == 0)) { -+ value = wait_event_interruptible (done.wait, done.done); -+ if (value != 0) { -+ spin_lock_irq (&epdata->dev->lock); -+ if (likely (epdata->ep != NULL)) { -+ DBG (epdata->dev, "%s i/o interrupted\n", -+ epdata->name); -+ usb_ep_dequeue (epdata->ep, epdata->req); -+ spin_unlock_irq (&epdata->dev->lock); -+ -+ wait_event (done.wait, done.done); -+ if (epdata->status == -ECONNRESET) -+ epdata->status = -EINTR; -+ } else { -+ spin_unlock_irq (&epdata->dev->lock); -+ -+ DBG (epdata->dev, "endpoint gone\n"); -+ epdata->status = -ENODEV; -+ } -+ } -+ return epdata->status; -+ } -+ return value; -+} -+ -+ -+/* handle a synchronous OUT bulk/intr/iso transfer */ -+static ssize_t -+ep_read (struct file *fd, char *buf, size_t len, loff_t *ptr) -+{ -+ struct ep_data *data = fd->private_data; -+ void *kbuf; -+ ssize_t value; -+ -+ if ((value = get_ready_ep (fd->f_flags, data)) < 0) -+ return value; -+ -+ /* halt any endpoint by doing a "wrong direction" i/o call */ -+ if (data->desc.bEndpointAddress & USB_DIR_IN) { -+ if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) -+ == USB_ENDPOINT_XFER_ISOC) -+ return -EINVAL; -+ DBG (data->dev, "%s halt\n", data->name); -+ spin_lock_irq (&data->dev->lock); -+ if (likely (data->ep != NULL)) -+ usb_ep_set_halt (data->ep); -+ spin_unlock_irq (&data->dev->lock); -+ up (&data->lock); -+ return -EBADMSG; -+ } -+ -+ /* FIXME readahead for O_NONBLOCK and poll(); careful with ZLPs */ -+ -+ value = -ENOMEM; -+ kbuf = kmalloc (len, SLAB_KERNEL); -+ if (unlikely (!kbuf)) -+ goto free1; -+ -+ value = ep_io (data, kbuf, len); -+ VDEBUG (data->dev, "%s read %d OUT, status %d\n", -+ data->name, len, value); -+ if (value >= 0 && copy_to_user (buf, kbuf, value)) -+ value = -EFAULT; -+ -+free1: -+ up (&data->lock); -+ kfree (kbuf); -+ return value; -+} -+ -+/* handle a synchronous IN bulk/intr/iso transfer */ -+static ssize_t -+ep_write (struct file *fd, const char *buf, size_t len, loff_t *ptr) -+{ -+ struct ep_data *data = fd->private_data; -+ void *kbuf; -+ ssize_t value; -+ -+ if ((value = get_ready_ep (fd->f_flags, data)) < 0) -+ return value; -+ -+ /* halt any endpoint by doing a "wrong direction" i/o call */ -+ if (!(data->desc.bEndpointAddress & USB_DIR_IN)) { -+ if ((data->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) -+ == USB_ENDPOINT_XFER_ISOC) -+ return -EINVAL; -+ DBG (data->dev, "%s halt\n", data->name); -+ spin_lock_irq (&data->dev->lock); -+ if (likely (data->ep != NULL)) -+ usb_ep_set_halt (data->ep); -+ spin_unlock_irq (&data->dev->lock); -+ up (&data->lock); -+ return -EBADMSG; -+ } -+ -+ /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */ -+ -+ value = -ENOMEM; -+ kbuf = kmalloc (len, SLAB_KERNEL); -+ if (!kbuf) -+ goto free1; -+ if (copy_from_user (kbuf, buf, len)) { -+ value = -EFAULT; -+ goto free1; -+ } -+ -+ value = ep_io (data, kbuf, len); -+ VDEBUG (data->dev, "%s write %d IN, status %d\n", -+ data->name, len, value); -+free1: -+ up (&data->lock); -+ kfree (kbuf); -+ return value; -+} -+ -+static int -+ep_release (struct inode *inode, struct file *fd) -+{ -+ struct ep_data *data = fd->private_data; -+ -+ /* clean up if this can be reopened */ -+ if (data->state != STATE_EP_UNBOUND) { -+ data->state = STATE_EP_DISABLED; -+ data->desc.bDescriptorType = 0; -+ data->hs_desc.bDescriptorType = 0; -+ } -+ put_ep (data); -+ return 0; -+} -+ -+static int ep_ioctl (struct inode *inode, struct file *fd, -+ unsigned code, unsigned long value) -+{ -+ struct ep_data *data = fd->private_data; -+ int status; -+ -+ if ((status = get_ready_ep (fd->f_flags, data)) < 0) -+ return status; -+ -+ spin_lock_irq (&data->dev->lock); -+ if (likely (data->ep != NULL)) { -+ switch (code) { -+ case GADGETFS_FIFO_STATUS: -+ status = usb_ep_fifo_status (data->ep); -+ break; -+ case GADGETFS_FIFO_FLUSH: -+ usb_ep_fifo_flush (data->ep); -+ break; -+ case GADGETFS_CLEAR_HALT: -+ status = usb_ep_clear_halt (data->ep); -+ break; -+ default: -+ status = -ENOTTY; -+ } -+ } else -+ status = -ENODEV; -+ spin_unlock_irq (&data->dev->lock); -+ up (&data->lock); -+ return status; -+} -+ -+/* used after endpoint configuration */ -+static struct file_operations ep_io_operations = { -+ .owner = THIS_MODULE, -+ .read = ep_read, -+ .write = ep_write, -+ .ioctl = ep_ioctl, -+ .release = ep_release, -+ -+ // .aio_read = ep_aio_read, -+ // .aio_write = ep_aio_write, -+}; -+ -+/* ENDPOINT INITIALIZATION -+ * -+ * fd = open ("/dev/gadget/$ENDPOINT", O_RDWR) -+ * status = write (fd, descriptors, sizeof descriptors) -+ * -+ * That write establishes the endpoint configuration, configuring -+ * the controller to process bulk, interrupt, or isochronous transfers -+ * at the right maxpacket size, and so on. -+ * -+ * The descriptors are message type 1, identified by a host order u32 -+ * at the beginning of what's written. Descriptor order is: full/low -+ * speed descriptor, then optional high speed descriptor. -+ */ -+static ssize_t -+ep_config (struct file *fd, const char *buf, size_t len, loff_t *ptr) -+{ -+ struct ep_data *data = fd->private_data; -+ struct usb_ep *ep; -+ u32 tag; -+ int value; -+ -+ if ((value = down_interruptible (&data->lock)) < 0) -+ return value; -+ -+ if (data->state != STATE_EP_READY) { -+ value = -EL2HLT; -+ goto fail; -+ } -+ -+ value = len; -+ if (len < USB_DT_ENDPOINT_SIZE + 4) -+ goto fail0; -+ -+ /* we might need to change message format someday */ -+ if (copy_from_user (&tag, buf, 4)) { -+ goto fail1; -+ } -+ if (tag != 1) { -+ DBG(data->dev, "config %s, bad tag %d\n", data->name, tag); -+ goto fail0; -+ } -+ buf += 4; -+ len -= 4; -+ -+ /* NOTE: audio endpoint extensions not accepted here; -+ * just don't include the extra bytes. -+ */ -+ -+ /* full/low speed descriptor, then high speed */ -+ if (copy_from_user (&data->desc, buf, USB_DT_ENDPOINT_SIZE)) { -+ goto fail1; -+ } -+ if (data->desc.bLength != USB_DT_ENDPOINT_SIZE -+ || data->desc.bDescriptorType != USB_DT_ENDPOINT) -+ goto fail0; -+ if (len != USB_DT_ENDPOINT_SIZE) { -+ if (len != 2 * USB_DT_ENDPOINT_SIZE) -+ goto fail0; -+ if (copy_from_user (&data->hs_desc, buf + USB_DT_ENDPOINT_SIZE, -+ USB_DT_ENDPOINT_SIZE)) { -+ goto fail1; -+ } -+ if (data->hs_desc.bLength != USB_DT_ENDPOINT_SIZE -+ || data->hs_desc.bDescriptorType -+ != USB_DT_ENDPOINT) { -+ DBG(data->dev, "config %s, bad hs length or type\n", -+ data->name); -+ goto fail0; -+ } -+ } -+ value = len; -+ -+ spin_lock_irq (&data->dev->lock); -+ if (data->dev->state == STATE_DEV_UNBOUND) { -+ value = -ENOENT; -+ goto gone; -+ } else if ((ep = data->ep) == NULL) { -+ value = -ENODEV; -+ goto gone; -+ } -+ switch (data->dev->gadget->speed) { -+ case USB_SPEED_LOW: -+ case USB_SPEED_FULL: -+ value = usb_ep_enable (ep, &data->desc); -+ if (value == 0) -+ data->state = STATE_EP_ENABLED; -+ break; -+#ifdef HIGHSPEED -+ case USB_SPEED_HIGH: -+ /* fails if caller didn't provide that descriptor... */ -+ value = usb_ep_enable (ep, &data->hs_desc); -+ if (value == 0) -+ data->state = STATE_EP_ENABLED; -+ break; -+#endif -+ default: -+ DBG (data->dev, "unconnected, %s init deferred\n", -+ data->name); -+ data->state = STATE_EP_DEFER_ENABLE; -+ } -+ if (value == 0) -+ fd->f_op = &ep_io_operations; -+gone: -+ spin_unlock_irq (&data->dev->lock); -+ if (value < 0) { -+fail: -+ data->desc.bDescriptorType = 0; -+ data->hs_desc.bDescriptorType = 0; -+ } -+ up (&data->lock); -+ return value; -+fail0: -+ value = -EINVAL; -+ goto fail; -+fail1: -+ value = -EFAULT; -+ goto fail; -+} -+ -+static int -+ep_open (struct inode *inode, struct file *fd) -+{ -+ struct ep_data *data = inode->u.generic_ip; -+ int value = -EBUSY; -+ -+ if (down_interruptible (&data->lock) != 0) -+ return -EINTR; -+ spin_lock_irq (&data->dev->lock); -+ if (data->dev->state == STATE_DEV_UNBOUND) -+ value = -ENOENT; -+ else if (data->state == STATE_EP_DISABLED) { -+ value = 0; -+ data->state = STATE_EP_READY; -+ get_ep (data); -+ fd->private_data = data; -+ VDEBUG (data->dev, "%s ready\n", data->name); -+ } else -+ DBG (data->dev, "%s state %d\n", -+ data->name, data->state); -+ spin_unlock_irq (&data->dev->lock); -+ up (&data->lock); -+ return value; -+} -+ -+/* used before endpoint configuration */ -+static struct file_operations ep_config_operations = { -+ .owner = THIS_MODULE, -+ .open = ep_open, -+ .write = ep_config, -+ .release = ep_release, -+}; -+ -+/*----------------------------------------------------------------------*/ -+ -+/* EP0 IMPLEMENTATION can be partly in userspace. -+ * -+ * Drivers that use this facility receive various events, including -+ * control requests the kernel doesn't handle. Drivers that don't -+ * use this facility may be too simple-minded for real applications. -+ */ -+ -+static inline void ep0_readable (struct dev_data *dev) -+{ -+ wake_up (&dev->wait); -+ kill_fasync (&dev->fasync, SIGIO, POLL_IN); -+} -+ -+static void clean_req (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct dev_data *dev = ep->driver_data; -+ -+ if (req->buf != dev->rbuf) { -+ usb_ep_free_buffer (ep, req->buf, req->dma, req->length); -+ req->buf = dev->rbuf; -+ req->dma = DMA_ADDR_INVALID; -+ } -+ req->complete = epio_complete; -+ dev->setup_out_ready = 0; -+} -+ -+static void ep0_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct dev_data *dev = ep->driver_data; -+ int free = 1; -+ -+ /* for control OUT, data must still get to userspace */ -+ if (!dev->setup_in) { -+ dev->setup_out_error = (req->status != 0); -+ if (!dev->setup_out_error) -+ free = 0; -+ dev->setup_out_ready = 1; -+ ep0_readable (dev); -+ } else if (dev->state == STATE_SETUP) -+ dev->state = STATE_CONNECTED; -+ -+ /* clean up as appropriate */ -+ if (free && req->buf != &dev->rbuf) -+ clean_req (ep, req); -+ req->complete = epio_complete; -+} -+ -+static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len) -+{ -+ struct dev_data *dev = ep->driver_data; -+ -+ if (dev->setup_out_ready) { -+ DBG (dev, "ep0 request busy!\n"); -+ return -EBUSY; -+ } -+ if (len > sizeof (dev->rbuf)) -+ req->buf = usb_ep_alloc_buffer (ep, len, &req->dma, GFP_ATOMIC); -+ if (req->buf == 0) { -+ req->buf = dev->rbuf; -+ return -ENOMEM; -+ } -+ req->complete = ep0_complete; -+ req->length = len; -+ return 0; -+} -+ -+static ssize_t -+ep0_read (struct file *fd, char *buf, size_t len, loff_t *ptr) -+{ -+ struct dev_data *dev = fd->private_data; -+ ssize_t retval; -+ enum ep0_state state; -+ -+ spin_lock_irq (&dev->lock); -+ -+ /* report fd mode change before acting on it */ -+ if (dev->setup_abort) { -+ dev->setup_abort = 0; -+ retval = -EIDRM; -+ goto done; -+ } -+ -+ /* control DATA stage */ -+ if ((state = dev->state) == STATE_SETUP) { -+ -+ if (dev->setup_in) { /* stall IN */ -+ VDEBUG(dev, "ep0in stall\n"); -+ (void) usb_ep_set_halt (dev->gadget->ep0); -+ retval = -EL2HLT; -+ dev->state = STATE_CONNECTED; -+ -+ } else if (len == 0) { /* ack SET_CONFIGURATION etc */ -+ struct usb_ep *ep = dev->gadget->ep0; -+ struct usb_request *req = dev->req; -+ -+ if ((retval = setup_req (ep, req, 0)) == 0) -+ retval = usb_ep_queue (ep, req, GFP_ATOMIC); -+ dev->state = STATE_CONNECTED; -+ -+ } else { /* collect OUT data */ -+ if ((fd->f_flags & O_NONBLOCK) != 0 -+ && !dev->setup_out_ready) { -+ retval = -EAGAIN; -+ goto done; -+ } -+ spin_unlock_irq (&dev->lock); -+ retval = wait_event_interruptible (dev->wait, -+ dev->setup_out_ready != 0); -+ -+ /* FIXME state could change from under us */ -+ spin_lock_irq (&dev->lock); -+ if (retval) -+ goto done; -+ if (dev->setup_out_error) -+ retval = -EIO; -+ else { -+ len = min (len, dev->req->actual); -+// FIXME don't call this with the spinlock held ... -+ if (copy_to_user (buf, &dev->req->buf, len)) -+ retval = -EFAULT; -+ clean_req (dev->gadget->ep0, dev->req); -+ /* NOTE userspace can't yet choose to stall */ -+ } -+ } -+ goto done; -+ } -+ -+ /* else normal: return event data */ -+ if (len < sizeof dev->event [0]) { -+ retval = -EINVAL; -+ goto done; -+ } -+ len -= len % sizeof (struct usb_gadgetfs_event); -+ dev->usermode_setup = 1; -+ -+scan: -+ /* return queued events right away */ -+ if (dev->ev_next != 0) { -+ unsigned i, n; -+ int tmp = dev->ev_next; -+ -+ len = min (len, tmp * sizeof (struct usb_gadgetfs_event)); -+ n = len / sizeof (struct usb_gadgetfs_event); -+ -+ /* ep0 can't deliver events when STATE_SETUP */ -+ for (i = 0; i < n; i++) { -+ if (dev->event [i].type == GADGETFS_SETUP) { -+ len = n = i + 1; -+ len *= sizeof (struct usb_gadgetfs_event); -+ n = 0; -+ break; -+ } -+ } -+ spin_unlock_irq (&dev->lock); -+ if (copy_to_user (buf, &dev->event, len)) -+ retval = -EFAULT; -+ else -+ retval = len; -+ if (len > 0) { -+ len /= sizeof (struct usb_gadgetfs_event); -+ -+ /* NOTE this doesn't guard against broken drivers; -+ * concurrent ep0 readers may lose events. -+ */ -+ spin_lock_irq (&dev->lock); -+ dev->ev_next -= len; -+ if (dev->ev_next != 0) -+ memmove (&dev->event, &dev->event [len], -+ sizeof (struct usb_gadgetfs_event) -+ * (tmp - len)); -+ if (n == 0) -+ dev->state = STATE_SETUP; -+ spin_unlock_irq (&dev->lock); -+ } -+ return retval; -+ } -+ if (fd->f_flags & O_NONBLOCK) { -+ retval = -EAGAIN; -+ goto done; -+ } -+ -+ switch (state) { -+ default: -+ DBG (dev, "fail %s, state %d\n", __FUNCTION__, state); -+ retval = -ESRCH; -+ break; -+ case STATE_UNCONNECTED: -+ case STATE_CONNECTED: -+ spin_unlock_irq (&dev->lock); -+ DBG (dev, "%s wait\n", __FUNCTION__); -+ -+ /* wait for events */ -+ retval = wait_event_interruptible (dev->wait, -+ dev->ev_next != 0); -+ if (retval < 0) -+ return retval; -+ spin_lock_irq (&dev->lock); -+ goto scan; -+ } -+ -+done: -+ spin_unlock_irq (&dev->lock); -+ return retval; -+} -+ -+static struct usb_gadgetfs_event * -+next_event (struct dev_data *dev, enum usb_gadgetfs_event_type type) -+{ -+ struct usb_gadgetfs_event *event; -+ unsigned i; -+ -+ switch (type) { -+ /* these events purge the queue */ -+ case GADGETFS_DISCONNECT: -+ if (dev->state == STATE_SETUP) -+ dev->setup_abort = 1; -+ // FALL THROUGH -+ case GADGETFS_CONNECT: -+ dev->ev_next = 0; -+ break; -+ case GADGETFS_SETUP: /* previous request timed out */ -+ case GADGETFS_SUSPEND: /* same effect */ -+ /* these events can't be repeated */ -+ for (i = 0; i != dev->ev_next; i++) { -+ if (dev->event [i].type != type) -+ continue; -+ DBG (dev, "discard old event %d\n", type); -+ dev->ev_next--; -+ if (i == dev->ev_next) -+ break; -+ /* indices start at zero, for simplicity */ -+ memmove (&dev->event [i], &dev->event [i + 1], -+ sizeof (struct usb_gadgetfs_event) -+ * (dev->ev_next - i)); -+ } -+ break; -+ default: -+ BUG (); -+ } -+ event = &dev->event [dev->ev_next++]; -+ BUG_ON (dev->ev_next > N_EVENT); -+ VDEBUG (dev, "ev %d, next %d\n", type, dev->ev_next); -+ memset (event, 0, sizeof *event); -+ event->type = type; -+ return event; -+} -+ -+static ssize_t -+ep0_write (struct file *fd, const char *buf, size_t len, loff_t *ptr) -+{ -+ struct dev_data *dev = fd->private_data; -+ ssize_t retval = -ESRCH; -+ -+ spin_lock_irq (&dev->lock); -+ -+ /* report fd mode change before acting on it */ -+ if (dev->setup_abort) { -+ dev->setup_abort = 0; -+ retval = -EIDRM; -+ -+ /* data and/or status stage for control request */ -+ } else if (dev->state == STATE_SETUP) { -+ -+ /* IN DATA+STATUS caller makes len <= wLength */ -+ if (dev->setup_in) { -+ retval = setup_req (dev->gadget->ep0, dev->req, len); -+ if (retval == 0) { -+ spin_unlock_irq (&dev->lock); -+ if (copy_from_user (dev->req->buf, buf, len)) -+ retval = -EFAULT; -+ else -+ retval = usb_ep_queue ( -+ dev->gadget->ep0, dev->req, -+ GFP_KERNEL); -+ if (retval < 0) { -+ spin_lock_irq (&dev->lock); -+ clean_req (dev->gadget->ep0, dev->req); -+ spin_unlock_irq (&dev->lock); -+ } else -+ retval = len; -+ -+ return retval; -+ } -+ -+ /* can stall some OUT transfers */ -+ } else if (dev->setup_can_stall) { -+ VDEBUG(dev, "ep0out stall\n"); -+ (void) usb_ep_set_halt (dev->gadget->ep0); -+ retval = -EL2HLT; -+ dev->state = STATE_CONNECTED; -+ } else { -+ DBG(dev, "bogus ep0out stall!\n"); -+ } -+ } else -+ DBG (dev, "fail %s, state %d\n", __FUNCTION__, dev->state); -+ -+ spin_unlock_irq (&dev->lock); -+ return retval; -+} -+ -+static int -+ep0_fasync (int f, struct file *fd, int on) -+{ -+ struct dev_data *dev = fd->private_data; -+ // caller must F_SETOWN before signal delivery happens -+ VDEBUG (dev, "%s %s\n", __FUNCTION__, on ? "on" : "off"); -+ return fasync_helper (f, fd, on, &dev->fasync); -+} -+ -+static struct usb_gadget_driver gadgetfs_driver; -+ -+static int -+dev_release (struct inode *inode, struct file *fd) -+{ -+ struct dev_data *dev = fd->private_data; -+ -+ /* closing ep0 === shutdown all */ -+ -+ usb_gadget_unregister_driver (&gadgetfs_driver); -+ -+ /* at this point "good" hardware has disconnected the -+ * device from USB; the host won't see it any more. -+ * alternatively, all host requests will time out. -+ */ -+ -+ fasync_helper (-1, fd, 0, &dev->fasync); -+ kfree (dev->buf); -+ dev->buf = 0; -+ put_dev (dev); -+ -+ /* other endpoints were all decoupled from this device */ -+ dev->state = STATE_DEV_DISABLED; -+ return 0; -+} -+ -+static int dev_ioctl (struct inode *inode, struct file *fd, -+ unsigned code, unsigned long value) -+{ -+ struct dev_data *dev = fd->private_data; -+ struct usb_gadget *gadget = dev->gadget; -+ -+ if (gadget->ops->ioctl) -+ return gadget->ops->ioctl (gadget, code, value); -+ return -ENOTTY; -+} -+ -+/* used after device configuration */ -+static struct file_operations ep0_io_operations = { -+ .owner = THIS_MODULE, -+ .read = ep0_read, -+ .write = ep0_write, -+ .fasync = ep0_fasync, -+ // .poll = ep0_poll, -+ .ioctl = dev_ioctl, -+ .release = dev_release, -+}; -+ -+/*----------------------------------------------------------------------*/ -+ -+/* The in-kernel gadget driver handles most ep0 issues, in particular -+ * enumerating the single configuration (as provided from user space). -+ * -+ * Unrecognized ep0 requests may be handled in user space. -+ */ -+ -+#ifdef HIGHSPEED -+static void make_qualifier (struct dev_data *dev) -+{ -+ struct usb_qualifier_descriptor qual; -+ struct usb_device_descriptor *desc; -+ -+ qual.bLength = sizeof qual; -+ qual.bDescriptorType = USB_DT_DEVICE_QUALIFIER; -+ qual.bcdUSB = __constant_cpu_to_le16 (0x0200); -+ -+ desc = dev->dev; -+ qual.bDeviceClass = desc->bDeviceClass; -+ qual.bDeviceSubClass = desc->bDeviceSubClass; -+ qual.bDeviceProtocol = desc->bDeviceProtocol; -+ -+ /* assumes ep0 uses the same value for both speeds ... */ -+ qual.bMaxPacketSize0 = desc->bMaxPacketSize0; -+ -+ qual.bNumConfigurations = 1; -+ qual.bRESERVED = 0; -+ -+ memcpy (dev->rbuf, &qual, sizeof qual); -+} -+#endif -+ -+static int -+config_buf (struct dev_data *dev, u8 type, unsigned index) -+{ -+ int len; -+#ifdef HIGHSPEED -+ int hs; -+#endif -+ -+ /* only one configuration */ -+ if (index > 0) -+ return -EINVAL; -+ -+#ifdef HIGHSPEED -+ hs = (dev->gadget->speed == USB_SPEED_HIGH); -+ if (type == USB_DT_OTHER_SPEED_CONFIG) -+ hs = !hs; -+ if (hs) { -+ dev->req->buf = dev->hs_config; -+ len = le16_to_cpup (&dev->hs_config->wTotalLength); -+ } else -+#endif -+ { -+ dev->req->buf = dev->config; -+ len = le16_to_cpup (&dev->config->wTotalLength); -+ } -+ ((u8 *)dev->req->buf) [1] = type; -+ return len; -+} -+ -+static int -+gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) -+{ -+ struct dev_data *dev = get_gadget_data (gadget); -+ struct usb_request *req = dev->req; -+ int value = -EOPNOTSUPP; -+ struct usb_gadgetfs_event *event; -+ -+ spin_lock (&dev->lock); -+ dev->setup_abort = 0; -+ if (dev->state == STATE_UNCONNECTED) { -+ struct usb_ep *ep; -+ struct ep_data *data; -+ -+ dev->state = STATE_CONNECTED; -+ dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; -+ -+#ifdef HIGHSPEED -+ if (gadget->speed == USB_SPEED_HIGH && dev->hs_config == 0) { -+ ERROR (dev, "no high speed config??\n"); -+ return -EINVAL; -+ } -+#endif /* HIGHSPEED */ -+ -+ INFO (dev, "connected\n"); -+ event = next_event (dev, GADGETFS_CONNECT); -+ event->u.speed = gadget->speed; -+ ep0_readable (dev); -+ -+ list_for_each_entry (ep, &gadget->ep_list, ep_list) { -+ data = ep->driver_data; -+ /* ... down_trylock (&data->lock) ... */ -+ if (data->state != STATE_EP_DEFER_ENABLE) -+ continue; -+#ifdef HIGHSPEED -+ if (gadget->speed == USB_SPEED_HIGH) -+ value = usb_ep_enable (ep, &data->hs_desc); -+ else -+#endif /* HIGHSPEED */ -+ value = usb_ep_enable (ep, &data->desc); -+ if (value) { -+ ERROR (dev, "deferred %s enable --> %d\n", -+ data->name, value); -+ continue; -+ } -+ data->state = STATE_EP_ENABLED; -+ wake_up (&data->wait); -+ DBG (dev, "woke up %s waiters\n", data->name); -+ } -+ -+ /* host may have given up waiting for response. we can miss control -+ * requests handled lower down (device/endpoint status and features); -+ * then ep0_{read,write} will report the wrong status. controller -+ * driver will have aborted pending i/o. -+ */ -+ } else if (dev->state == STATE_SETUP) -+ dev->setup_abort = 1; -+ -+ req->buf = dev->rbuf; -+ req->dma = DMA_ADDR_INVALID; -+ req->context = 0; -+ value = -EOPNOTSUPP; -+ switch (ctrl->bRequest) { -+ -+ case USB_REQ_GET_DESCRIPTOR: -+ if (ctrl->bRequestType != USB_DIR_IN) -+ goto unrecognized; -+ switch (ctrl->wValue >> 8) { -+ -+ case USB_DT_DEVICE: -+ value = min (ctrl->wLength, (u16) sizeof *dev->dev); -+ req->buf = dev->dev; -+ break; -+#ifdef HIGHSPEED -+ case USB_DT_DEVICE_QUALIFIER: -+ if (!dev->hs_config) -+ break; -+ value = min (ctrl->wLength, (u16) -+ sizeof (struct usb_qualifier_descriptor)); -+ make_qualifier (dev); -+ break; -+ case USB_DT_OTHER_SPEED_CONFIG: -+ // FALLTHROUGH -+#endif -+ case USB_DT_CONFIG: -+ value = config_buf (dev, -+ ctrl->wValue >> 8, -+ ctrl->wValue & 0xff); -+ if (value >= 0) -+ value = min (ctrl->wLength, (u16) value); -+ break; -+ case USB_DT_STRING: -+ goto unrecognized; -+ -+ default: // all others are errors -+ break; -+ } -+ break; -+ -+ /* currently one config, two speeds */ -+ case USB_REQ_SET_CONFIGURATION: -+ if (ctrl->bRequestType != 0) -+ break; -+ if (0 == (u8) ctrl->wValue) { -+ value = 0; -+ dev->current_config = 0; -+ // user mode expected to disable endpoints -+ } else { -+ u8 config; -+#ifdef HIGHSPEED -+ if (gadget->speed == USB_SPEED_HIGH) -+ config = dev->hs_config->bConfigurationValue; -+ else -+#endif -+ config = dev->config->bConfigurationValue; -+ -+ if (config == (u8) ctrl->wValue) { -+ value = 0; -+ dev->current_config = config; -+ } -+ } -+ -+ /* report SET_CONFIGURATION like any other control request, -+ * except that usermode may not stall this. the next -+ * request mustn't be allowed start until this finishes: -+ * endpoints and threads set up, etc. -+ * -+ * NOTE: older PXA hardware (before PXA 255: without UDCCFR) -+ * has bad/racey automagic that prevents synchronizing here. -+ * even kernel mode drivers often miss them. -+ */ -+ if (value == 0) { -+ INFO (dev, "configuration #%d\n", dev->current_config); -+ if (dev->usermode_setup) { -+ dev->setup_can_stall = 0; -+ goto delegate; -+ } -+ } -+ break; -+ -+#ifndef CONFIG_USB_GADGETFS_PXA2XX -+ /* PXA automagically handles this request too */ -+ case USB_REQ_GET_CONFIGURATION: -+ if (ctrl->bRequestType != 0x80) -+ break; -+ *(u8 *)req->buf = dev->current_config; -+ value = min (ctrl->wLength, (u16) 1); -+ break; -+#endif -+ -+ default: -+unrecognized: -+ VDEBUG (dev, "%s req%02x.%02x v%04x i%04x l%d\n", -+ dev->usermode_setup ? "delegate" : "fail", -+ ctrl->bRequestType, ctrl->bRequest, -+ ctrl->wValue, ctrl->wIndex, ctrl->wLength); -+ -+ /* if there's an ep0 reader, don't stall */ -+ if (dev->usermode_setup) { -+ dev->setup_can_stall = 1; -+delegate: -+ dev->setup_in = (ctrl->bRequestType & USB_DIR_IN) -+ ? 1 : 0; -+ dev->setup_out_ready = 0; -+ dev->setup_out_error = 0; -+ value = 0; -+ -+ /* read DATA stage for OUT right away */ -+ if (unlikely (!dev->setup_in && ctrl->wLength)) { -+ value = setup_req (gadget->ep0, dev->req, -+ ctrl->wLength); -+ if (value < 0) -+ break; -+ value = usb_ep_queue (gadget->ep0, dev->req, -+ GFP_ATOMIC); -+ if (value < 0) { -+ clean_req (gadget->ep0, dev->req); -+ break; -+ } -+ -+ /* we can't currently stall these */ -+ dev->setup_can_stall = 0; -+ } -+ -+ /* state changes when reader collects event */ -+ event = next_event (dev, GADGETFS_SETUP); -+ event->u.setup = *ctrl; -+ ep0_readable (dev); -+ spin_unlock (&dev->lock); -+ return 0; -+ } -+ } -+ -+ /* proceed with data transfer and status phases? */ -+ if (value >= 0 && dev->state != STATE_SETUP) { -+ req->length = value; -+ value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); -+ if (value < 0) { -+ DBG (dev, "ep_queue --> %d\n", value); -+ req->status = 0; -+ } -+ } -+ -+ /* device stalls when value < 0 */ -+ spin_unlock (&dev->lock); -+ return value; -+} -+ -+static void destroy_ep_files (struct dev_data *dev) -+{ -+ struct list_head *entry, *tmp; -+ -+ DBG (dev, "%s %d\n", __FUNCTION__, dev->state); -+ -+ /* dev->state must prevent interference */ -+restart: -+ spin_lock_irq (&dev->lock); -+ list_for_each_safe (entry, tmp, &dev->epfiles) { -+ struct ep_data *ep; -+ -+ /* break link to FS */ -+ ep = list_entry (entry, struct ep_data, epfiles); -+ list_del_init (&ep->epfiles); -+ -+ /* break link to controller */ -+ if (ep->state == STATE_EP_ENABLED) -+ (void) usb_ep_disable (ep->ep); -+ ep->state = STATE_EP_UNBOUND; -+ usb_ep_free_request (ep->ep, ep->req); -+ ep->ep = 0; -+ wake_up (&ep->wait); -+ put_ep (ep); -+ -+ spin_unlock_irq (&dev->lock); -+ -+ /* fds may still be open */ -+ goto restart; -+ } -+ spin_unlock_irq (&dev->lock); -+} -+ -+ -+static int activate_ep_files (struct dev_data *dev) -+{ -+ struct usb_ep *ep; -+ -+ gadget_for_each_ep (ep, dev->gadget) { -+ struct ep_data *data; -+ -+ data = kmalloc (sizeof *data, GFP_KERNEL); -+ if (!data) -+ goto enomem; -+ memset (data, 0, sizeof data); -+ data->state = STATE_EP_DISABLED; -+ init_MUTEX (&data->lock); -+ init_waitqueue_head (&data->wait); -+ -+ strncpy (data->name, ep->name, sizeof (data->name) - 1); -+ atomic_set (&data->count, 1); -+ data->dev = dev; -+ get_dev (dev); -+ -+ data->ep = ep; -+ ep->driver_data = data; -+ -+ data->req = usb_ep_alloc_request (ep, GFP_KERNEL); -+ if (!data->req) -+ goto enomem; -+ -+ list_add_tail (&data->epfiles, &dev->epfiles); -+ } -+ return 0; -+ -+enomem: -+ DBG (dev, "%s enomem\n", __FUNCTION__); -+ destroy_ep_files (dev); -+ return -ENOMEM; -+} -+ -+static void -+gadgetfs_unbind (struct usb_gadget *gadget) -+{ -+ struct dev_data *dev = get_gadget_data (gadget); -+ -+ DBG (dev, "%s\n", __FUNCTION__); -+ -+ spin_lock_irq (&dev->lock); -+ dev->state = STATE_DEV_UNBOUND; -+ spin_unlock_irq (&dev->lock); -+ -+ destroy_ep_files (dev); -+ gadget->ep0->driver_data = 0; -+ set_gadget_data (gadget, 0); -+ -+ /* we've already been disconnected ... no i/o is active */ -+ if (dev->req) -+ usb_ep_free_request (gadget->ep0, dev->req); -+ DBG (dev, "%s done\n", __FUNCTION__); -+ put_dev (dev); -+} -+ -+static struct dev_data *the_device; -+ -+static int -+gadgetfs_bind (struct usb_gadget *gadget) -+{ -+ struct dev_data *dev = the_device; -+ -+ if (!dev) -+ return -ESRCH; -+ if (0 != strcmp (CHIP, gadget->name)) { -+ printk (KERN_ERR "%s expected " CHIP " controller not %s\n", -+ shortname, gadget->name); -+ return -ENODEV; -+ } -+ -+ set_gadget_data (gadget, dev); -+ dev->gadget = gadget; -+ gadget->ep0->driver_data = dev; -+ dev->dev->bMaxPacketSize0 = gadget->ep0->maxpacket; -+ -+ /* preallocate control response and buffer */ -+ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); -+ if (!dev->req) -+ goto enomem; -+ dev->req->context = 0; -+ dev->req->complete = epio_complete; -+ -+ if (activate_ep_files (dev) < 0) -+ goto enomem; -+ -+ INFO (dev, "bound to %s driver\n", gadget->name); -+ dev->state = STATE_UNCONNECTED; -+ get_dev (dev); -+ return 0; -+ -+enomem: -+ gadgetfs_unbind (gadget); -+ return -ENOMEM; -+} -+ -+static void -+gadgetfs_disconnect (struct usb_gadget *gadget) -+{ -+ struct dev_data *dev = get_gadget_data (gadget); -+ -+ if (dev->state == STATE_UNCONNECTED) { -+ DBG (dev, "already unconnected\n"); -+ return; -+ } -+ dev->state = STATE_UNCONNECTED; -+ -+ INFO (dev, "disconnected\n"); -+ spin_lock (&dev->lock); -+ next_event (dev, GADGETFS_DISCONNECT); -+ ep0_readable (dev); -+ spin_unlock (&dev->lock); -+} -+ -+static void -+gadgetfs_suspend (struct usb_gadget *gadget) -+{ -+ struct dev_data *dev = get_gadget_data (gadget); -+ -+ INFO (dev, "suspended from state %d\n", dev->state); -+ spin_lock (&dev->lock); -+ switch (dev->state) { -+ case STATE_SETUP: // VERY odd... host died?? -+ case STATE_CONNECTED: -+ case STATE_UNCONNECTED: -+ next_event (dev, GADGETFS_SUSPEND); -+ ep0_readable (dev); -+ /* FALLTHROUGH */ -+ default: -+ break; -+ } -+ spin_unlock (&dev->lock); -+} -+ -+static struct usb_gadget_driver gadgetfs_driver = { -+#ifdef HIGHSPEED -+ .speed = USB_SPEED_HIGH, -+#else -+ .speed = USB_SPEED_FULL, -+#endif -+ .function = (char *) driver_desc, -+ .bind = gadgetfs_bind, -+ .unbind = gadgetfs_unbind, -+ .setup = gadgetfs_setup, -+ .disconnect = gadgetfs_disconnect, -+ .suspend = gadgetfs_suspend, -+ -+ .driver = { -+ .name = (char *) shortname, -+ // .shutdown = ... -+ // .suspend = ... -+ // .resume = ... -+ }, -+}; -+ -+/*----------------------------------------------------------------------*/ -+ -+/* DEVICE INITIALIZATION -+ * -+ * fd = open ("/dev/gadget/$CHIP", O_RDWR) -+ * status = write (fd, descriptors, sizeof descriptors) -+ * -+ * That write establishes the device configuration, so the kernel can -+ * bind to the controller ... guaranteeing it can handle enumeration -+ * at all necessary speeds. Descriptor order is: -+ * -+ * . message tag (u32, host order) ... for now, must be zero; it -+ * would change to support features like multi-config devices -+ * . full/low speed config ... all wTotalLength bytes (with interface, -+ * class, altsetting, endpoint, and other descriptors) -+ * . high speed config ... all descriptors, for high speed operation; -+ * this one's optional except for high-speed hardware -+ * . device descriptor -+ * -+ * Endpoints are not yet enabled. Drivers may want to immediately -+ * initialize them, using the /dev/gadget/ep* files that are available -+ * as soon as the kernel sees the configuration, or they can wait -+ * until device configuration and interface altsetting changes create -+ * the need to configure (or unconfigure) them. -+ * -+ * After initialization, the device stays active for as long as that -+ * $CHIP file is open. Events may then be read from that descriptor, -+ * such configuration notifications. More complex drivers will handle -+ * some control requests in user space. -+ */ -+ -+static int is_valid_config (struct usb_config_descriptor *config) -+{ -+ return config->bDescriptorType == USB_DT_CONFIG -+ && config->bLength == USB_DT_CONFIG_SIZE -+ && config->bConfigurationValue != 0 -+ && (config->bmAttributes & USB_CONFIG_ATT_ONE) != 0 -+ && (config->bmAttributes & USB_CONFIG_ATT_WAKEUP) == 0; -+ /* FIXME check lengths: walk to end */ -+} -+ -+static ssize_t -+dev_config (struct file *fd, const char *buf, size_t len, loff_t *ptr) -+{ -+ struct dev_data *dev = fd->private_data; -+ ssize_t value = len, length = len; -+ unsigned total; -+ u32 tag; -+ char *kbuf; -+ -+ if (dev->state != STATE_OPENED) -+ return -EEXIST; -+ -+ if (len < (USB_DT_CONFIG_SIZE + USB_DT_DEVICE_SIZE + 4)) -+ return -EINVAL; -+ -+ /* we might need to change message format someday */ -+ if (copy_from_user (&tag, buf, 4)) -+ return -EFAULT; -+ if (tag != 0) -+ return -EINVAL; -+ buf += 4; -+ length -= 4; -+ -+ kbuf = kmalloc (length, SLAB_KERNEL); -+ if (!kbuf) -+ return -ENOMEM; -+ if (copy_from_user (kbuf, buf, length)) { -+ kfree (kbuf); -+ return -EFAULT; -+ } -+ -+ spin_lock_irq (&dev->lock); -+ value = -EINVAL; -+ if (dev->buf) -+ goto fail; -+ dev->buf = kbuf; -+ -+ /* full or low speed config */ -+ dev->config = (void *) kbuf; -+ total = le16_to_cpup (&dev->config->wTotalLength); -+ if (!is_valid_config (dev->config) || total >= length) -+ goto fail; -+ kbuf += total; -+ length -= total; -+ -+ /* optional high speed config */ -+ if (kbuf [1] == USB_DT_CONFIG) { -+ dev->hs_config = (void *) kbuf; -+ total = le16_to_cpup (&dev->hs_config->wTotalLength); -+ if (!is_valid_config (dev->hs_config) || total >= length) -+ goto fail; -+ kbuf += total; -+ length -= total; -+ } -+ -+ /* could support multiple configs, using another encoding! */ -+ -+ /* device descriptor (tweaked for paranoia) */ -+ if (length != USB_DT_DEVICE_SIZE) -+ goto fail; -+ dev->dev = (void *)kbuf; -+ if (dev->dev->bLength != USB_DT_DEVICE_SIZE -+ || dev->dev->bDescriptorType != USB_DT_DEVICE -+ || dev->dev->bNumConfigurations != 1) -+ goto fail; -+ dev->dev->bNumConfigurations = 1; -+ dev->dev->bcdUSB = __constant_cpu_to_le16 (0x0200); -+ -+ /* triggers gadgetfs_bind(); then we can enumerate. */ -+ spin_unlock_irq (&dev->lock); -+ value = usb_gadget_register_driver (&gadgetfs_driver); -+ if (value != 0) { -+ kfree (dev->buf); -+ dev->buf = 0; -+ } else { -+ /* at this point "good" hardware has for the first time -+ * let the USB the host see us. alternatively, if users -+ * unplug/replug that will clear all the error state. -+ * -+ * note: everything running before here was guaranteed -+ * to choke driver model style diagnostics. from here -+ * on, they can work ... except in cleanup paths that -+ * kick in after the ep0 descriptor is closed. -+ */ -+ fd->f_op = &ep0_io_operations; -+ value = len; -+ } -+ return value; -+ -+fail: -+ spin_unlock_irq (&dev->lock); -+ pr_debug ("%s: %s fail %d, %p\n", shortname, __FUNCTION__, value, dev); -+ kfree (dev->buf); -+ dev->buf = 0; -+ return value; -+} -+ -+static int -+dev_open (struct inode *inode, struct file *fd) -+{ -+ struct dev_data *dev = inode->u.generic_ip; -+ int value = -EBUSY; -+ -+ if (dev->state == STATE_DEV_DISABLED) { -+ dev->ev_next = 0; -+ dev->state = STATE_OPENED; -+ fd->private_data = dev; -+ get_dev (dev); -+ value = 0; -+ } -+ return value; -+} -+ -+static struct file_operations dev_init_operations = { -+ .owner = THIS_MODULE, -+ .open = dev_open, -+ .write = dev_config, -+ .fasync = ep0_fasync, -+ .ioctl = dev_ioctl, -+ .release = dev_release, -+}; -+ -+/*----------------------------------------------------------------------*/ -+ -+/* -+ * implementation for 2.4 uses character special files -+ * ep0/device file MKDEV (c_major, 0) -+ * first data ep MKDEV (c_major, 1) -+ * second data ep MKDEV (c_major, 2) -+ * ... -+ * -+ * FIXME can do it as a real filesystem on 2.4 too, without libfs -+ */ -+static int c_major = 240; /* 240 is local/experimental */ -+MODULE_PARM (c_major, "i"); -+MODULE_PARM_DESC (c_major, "major number for char special files"); -+ -+static int gadget_open (struct inode *ino, struct file *fp) -+{ -+ int num = minor (ino->i_rdev); -+ struct dev_data *dev; -+ struct file_operations *ops; -+ -+ /* ep0 file, "/dev/gadget/$CHIP" */ -+ if (num == 0) { -+ int status; -+ -+ if (the_device != 0) -+ return -EBUSY; -+ the_device = dev_new (); -+ if (the_device == 0) -+ return -ENOMEM; -+ -+ dev = the_device; -+ ino->u.generic_ip = dev; -+ ops = &dev_init_operations; -+ fp->f_op = ops; -+ -+ status = ops->open (ino, fp); -+ if (status < 0) { -+ put_dev (dev); -+ the_device = 0; -+ } -+ return status; -+ -+ /* ep files, "/dev/gadget/$ENDPOINT" */ -+ } else { -+ struct list_head *entry; -+ struct ep_data *data; -+ -+ /* unavailable till device is initted */ -+ dev = the_device; -+ if (dev == 0) -+ return -ENODEV; -+ -+ /* order in controller's name listing matters! */ -+ list_for_each (entry, &dev->epfiles) { -+ if (--num == 0) -+ goto found; -+ } -+ return -ENODEV; -+found: -+ data = list_entry (entry, struct ep_data, epfiles); -+ ino->u.generic_ip = data; -+ ops = &ep_config_operations; -+ fp->f_op = ops; -+ -+ return ops->open (ino, fp); -+ } -+} -+ -+static struct file_operations gadget_fops = { -+ .owner = THIS_MODULE, -+ .open = gadget_open, -+}; -+ -+/*----------------------------------------------------------------------*/ -+ -+static int __init init (void) -+{ -+ int status; -+ -+ status = register_chrdev (c_major, shortname, &gadget_fops); -+ if (status < 0) { -+ printk (KERN_WARNING "%s: can't get major %d\n", -+ shortname, c_major); -+ return status; -+ } -+ -+ /* dynamic assignment */ -+ if (c_major == 0) -+ c_major = status; -+ status = 0; -+ -+ pr_info ("%s: using char major %d\n", shortname, c_major); -+ -+ if (status == 0) -+ pr_info ("%s: %s, version " DRIVER_VERSION "\n", -+ shortname, driver_desc); -+ return status; -+} -+module_init (init); -+ -+static void __exit cleanup (void) -+{ -+ pr_debug ("unregister %s\n", shortname); -+ unregister_chrdev (c_major, shortname); -+} -+module_exit (cleanup); -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/n9604.c kernel/drivers/usb/gadget/n9604.c ---- /tmp/kernel/drivers/usb/gadget/n9604.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/n9604.c 2005-04-22 17:53:19.461535120 +0200 -@@ -0,0 +1,1088 @@ -+/* -+ * National 9603/4 USB Device Controller driver -+ * Copyright (C) 2004 Technical Solutions Inc. (support@techsol.ca) -+ * ported from : The Goku-S driver -+ * Copyright (C) 2003 MontaVista Software (source@mvista.com) -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+/* -+ * This device has ep0 and six semi-configurable bulk/interrupt endpoints. -+ * -+ * - Endpoint numbering is fixed: -+ * Endpoint 0: ep0 -+ * Endpoint 1: ep1in (tx) -+ * Endpoint 2: ep2out (rx) -+ * Endpoint 3: ep3in (tx) -+ * Endpoint 4: ep4out (rx) -+ * Endpoint 5: ep5in (tx) -+ * Endpoint 6: ep6out (rx) -+ */ -+ -+/* -+ * The ep->stage information refers to the state of a setup transaction -+ * -+ * state 0: no setup packet has been received -+ * state 1: setup packet has been received -+ * state 2: data has been sent/received -+ * state 3: ZLP has been received/sent -+ */ -+ -+#include <linux/config.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/delay.h> -+#include <linux/ioport.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/smp_lock.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include <asm/byteorder.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/unaligned.h> -+ -+#include "n9604.h" -+#include "n9604regs.h" -+ -+inline void Flush_and_enable(u8 control_reg) { -+ write_9604(RXC_FLUSH, control_reg); -+ while (read_9604(control_reg) & RXC_FLUSH); -+ write_9604(RXC_RX_EN, control_reg); -+} -+inline void Flush(u8 control_reg) { -+ write_9604(RXC_FLUSH, control_reg); -+ while (read_9604(control_reg) & RXC_FLUSH); -+} -+ -+#define DRIVER_DESC "N9604 USB Device Controller" -+#define DRIVER_VERSION "29-Oct 2004" -+ -+static const char driver_name [] = "n9604_udc"; -+static const char driver_desc [] = DRIVER_DESC; -+ -+MODULE_AUTHOR("support@techsol.ca"); -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_LICENSE("GPL"); -+ -+static void nuke(struct n9604_ep *ep, int status); -+inline void send_zero_length(int endpoint, struct n9604_udc *dev); -+ -+u8 * USBN9604_Offset; //device virtual address -+ -+/* FIXME all the IRQ stuff is board-specific -+ */ -+ -+#define h7201_readl readl -+#define h7201_writel writel -+ -+#define ETHER_IRQ_IP_OFFSET 0 -+#define ETHER_IRQ_BIT_POS 0 -+#define ETHER_IRQ_IM_OFFSET 0 -+ -+#define IRQ_GPIOC -1 -+ -+#define USBD_ENABLE_IRQ {h7201_writel( h7201_readl(ETHER_IRQ_IP_OFFSET) | (1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IP_OFFSET); h7201_writel( h7201_readl(ETHER_IRQ_IM_OFFSET) | (1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IM_OFFSET);} -+#define USBD_DISABLE_IRQ h7201_writel( h7201_readl(ETHER_IRQ_IM_OFFSET) & ~(1 << ETHER_IRQ_BIT_POS), ETHER_IRQ_IM_OFFSET); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+//enable an end point, of description desc -+static int n9604_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) { -+ struct n9604_udc *dev; -+ struct n9604_ep *ep; -+ u16 max; -+ -+ ep = container_of(_ep, struct n9604_ep, ep); -+ -+ if (!_ep || !desc || ep->desc || desc->bDescriptorType != USB_DT_ENDPOINT) -+ return -EINVAL; -+ -+ dev = ep->dev; -+ if (!ep->num) -+ return -EINVAL; -+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return -ESHUTDOWN; -+ if (ep->num && !(desc->bEndpointAddress & 0x0f)) -+ return -EINVAL; -+ -+ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { -+ case USB_ENDPOINT_XFER_BULK: -+ case USB_ENDPOINT_XFER_INT: -+ break; -+ default: -+ return -EINVAL; -+ } -+ -+ write_9604((ep->numActual & EPC_EP_MASK) | EPC_EP_EN | (EPC_ISO * ((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_ISOC)), ep->control); -+ if (ep->is_in) -+ Flush(ep->command); -+ else -+ Flush_and_enable(ep->command); -+ -+ max = le16_to_cpu(get_unaligned(&desc->wMaxPacketSize)); -+ ep->ep.maxpacket = min_t(u16, max, MAX_FIFO_SIZE); -+ ep->desc = desc; -+ -+ return 0; -+} -+ -+static int n9604_ep_disable(struct usb_ep *_ep)//ep > 0 -+{ -+ struct n9604_ep *ep; -+ struct n9604_udc *dev; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct n9604_ep, ep); -+ -+ if (!_ep || !ep->desc) -+ return -ENODEV; -+ dev = ep->dev; -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ nuke(ep, -ESHUTDOWN); -+ write_9604(0, ep->command); -+ ep->desc = NULL; -+ spin_unlock_irqrestore(&dev->lock, flags); -+ -+ return 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_request * -+n9604_alloc_request(struct usb_ep *_ep, int gfp_flags) -+{ -+ struct n9604_request *req; -+ -+ if (!_ep) -+ return 0; -+ req = kmalloc(sizeof *req, gfp_flags); -+ if (!req) -+ return 0; -+ -+ memset(req, 0, sizeof *req); -+ INIT_LIST_HEAD(&req->queue); -+ return &req->req; -+} -+ -+static void -+n9604_free_request(struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct n9604_request *req; -+ -+ if (!_ep || !_req) -+ return; -+ -+ req = container_of(_req, struct n9604_request, req); -+ WARN_ON(!list_empty(&req->queue)); -+ kfree(req); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void done(struct n9604_ep *ep, struct n9604_request *req, int status); -+ -+static inline int -+write_packet(struct n9604_ep *ep, u8 *buf, struct n9604_request *req) -+{ -+ unsigned written_length, desired_length, available_length, maximum_length, flags, loop_length; -+ -+ u8 fifo = ep->fifo; -+ u8 command = ep->command; -+ u8 status = ep->status; -+ if (!ep->num) { -+ fifo = TXD0; -+ command = TXC0; -+ status = TXS0; -+ } -+ -+ if (read_9604(command) & TXC_TX_EN) -+ return -EBUSY; -+ ep->packets++; -+ -+ desired_length = req->req.length - req->req.actual; -+ available_length = read_9604(status) & TXS_TCOUNT_MASK;//might be greater -+ written_length = 0; -+ if (ep->num) -+ maximum_length = MAX_FIFO_SIZE; -+ else -+ maximum_length = MAX_EP0_SIZE; -+ -+ while ((loop_length = min(min(available_length, desired_length), maximum_length))) { -+ int i = loop_length; -+ while (i) { write_9604(*buf++,fifo); i--; } -+ written_length += loop_length; -+ desired_length -= loop_length; -+ maximum_length -= loop_length; -+ if (desired_length && maximum_length)//check if really need to read the chip again -+ available_length = (read_9604(status) & TXS_TCOUNT_MASK); -+ } -+ -+ req->req.actual += written_length; -+ -+ flags = TXC_TX_EN; -+ if (ep->num) -+ flags |= TXC_LAST; -+ if (ep->toggle) -+ flags |= TXC_TOGGLE; -+ write_9604(flags, command); -+ ep->toggle = !(ep->toggle); -+ if (!written_length) req->req.zero = 0;//just wrote zero bytes, there is no more need for req.zero -+ return written_length; -+} -+ -+// return: 0 = still running, 1 = completed, negative = errno -+static int write_fifo(struct n9604_ep *ep, struct n9604_request *req) -+{ -+ struct n9604_udc *dev = ep->dev; -+ u8 *buf; -+ unsigned count; -+ int is_last; -+ -+ buf = req->req.buf + req->req.actual; -+ prefetch(buf); -+ -+ dev = ep->dev; -+ -+ count = write_packet(ep, buf, req); -+ if (count < 0) -+ return count; -+ -+ /* last packet often short (sometimes a zlp, especially on ep0) */ -+ if ((req->req.length != req->req.actual) || req->req.zero) -+ is_last = 0; -+ else -+ is_last = 1; -+ -+ /* requests complete when all IN data is in the FIFO, -+ * or sometimes later, if a zlp was needed. -+ */ -+ if (is_last) { -+ done(ep, req, 0); -+ return 1; -+ } -+ return 0; -+} -+ -+static inline void pio_irq_enable(struct n9604_ep *ep); -+ -+static int read_fifo(struct n9604_ep *ep, struct n9604_request *req) -+{ -+ u32 size; -+ u8 *buf; -+ int bufferspace_available, fifospace_left, num_bytes_read; -+ int fifo, status; -+ ep->packets++; -+ if (!ep->num) { -+ fifo = RXD0; -+ status = RXS0; -+ } else { -+ fifo = ep->fifo; -+ status = ep->status; -+ } -+ num_bytes_read = 0; -+ buf = req->req.buf + req->req.actual; -+ bufferspace_available = req->req.length - req->req.actual; -+ size = read_9604(status) & (RXS_RCOUNTMASK | RXS_RX_ERR);//number of bytes ready to be read (15 if greater than 15) -+ if (ep->num && (size & RXS_RX_ERR)) { -+ ERROR(ep->dev, "DATA ERROR!!!! on ep%d\nFlushing Fifo", ep->num); -+ Flush_and_enable(ep->command); -+ goto leave; -+ } -+ size = size & ~RXS_RX_ERR;//clear the bit -+ if (ep->num) fifospace_left = MAX_FIFO_SIZE; -+ else fifospace_left = MAX_EP0_SIZE; -+loop: -+ /* read all bytes from this packet */ -+ while (size-- != 0) { -+ u8 byte = read_9604(fifo); -+ if (unlikely(bufferspace_available == 0)) { -+ /* this happens when the driver's buffer -+ * is smaller than what the host sent. -+ * discard the extra data in this packet. -+ */ -+ done(ep, req, -EOVERFLOW); -+ return 1; -+ } else { -+ *buf++ = byte; -+ bufferspace_available--; -+ fifospace_left--; -+ num_bytes_read++; -+ } -+ } -+ if ((size = (read_9604(status) & RXS_RCOUNTMASK))) { -+ goto loop;//since there is more data -+ } -+ /* completion */ -+ req->req.actual = req->req.actual + num_bytes_read; -+ if (fifospace_left || req->req.actual == req->req.length) { -+ done(ep, req, 0); -+ return 1; -+ } -+leave: -+ pio_irq_enable(ep);//turn the interrupt back on -+ return 0; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static inline void -+pio_irq_enable(struct n9604_ep *ep) -+{ -+ if (ep->is_in) -+ write_9604(read_9604(TXMSK) | 1 << ep->fifoNum | 0x10 << ep->fifoNum, TXMSK); -+ else { -+ u8 command = ep->command; -+ if (!ep->num) command = RXC0; -+ write_9604(read_9604(RXMSK) | 1 << ep->fifoNum | 0x10 << ep->fifoNum, RXMSK); -+ write_9604(RXC_RX_EN | RXC_RFWL0 | RXC_RFWL1, command); -+ } -+} -+ -+static inline void -+pio_irq_disable(struct n9604_ep *ep)//epnum != 0 -+{ -+ if (ep->is_in) -+ write_9604(read_9604(TXMSK) & ~(1 << ep->fifoNum) & ~(0x10 << ep->fifoNum), TXMSK); -+ else -+ write_9604(read_9604(RXMSK) & ~(1 << ep->fifoNum) & ~(0x10 << ep->fifoNum), RXMSK); -+} -+ -+static int request_voodoo = 0;//number of bytes the host requested -+ -+static inline void -+pio_advance(struct n9604_ep *ep) -+{ -+ struct n9604_request *req; -+ -+ if (list_empty (&ep->queue)) { -+ if (!ep->num) { -+ if (ep->is_in && (ep->stage == 2)) { -+ ep->is_in = 0;//switch modes -+ Flush_and_enable(RXC0);//needed to receive a ZLP after tx -+ ep->stage++;//and bump the stage number -+ } else if (ep->stage == 3) { -+ ep->stage = 0; -+ } -+ } -+ return; -+ } -+ req = list_entry(ep->queue.next, struct n9604_request, queue); -+ (ep->is_in ? write_fifo : read_fifo)(ep, req); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void * n9604_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, int gfp_flags) -+{ -+ return kmalloc(bytes, gfp_flags); -+} -+ -+static void n9604_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) -+{ -+ kfree (buf); -+} -+ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void -+done(struct n9604_ep *ep, struct n9604_request *req, int status) -+{ -+ struct n9604_udc *dev; -+ -+ list_del_init(&req->queue); -+ ep->queue_active--; -+ -+ if (req->req.status == -EINPROGRESS) -+ req->req.status = status; -+ else -+ status = req->req.status; -+ -+ dev = ep->dev; -+ -+ /* don't modify queue heads during completion callback */ -+ if (ep->num) -+ pio_irq_disable(ep); -+ else if (!ep->nuking) { -+ ep->stage++; -+ ep->toggle = 1;//other endpoints stay in their flipping mode between transactions -+ if (ep->stage == 2) {//we are in stage 2 now -+ if (!ep->is_in) { -+ ep->is_in = 1;//switch modes -+ request_voodoo = 1;//prevents n9604_queue from calling us again before doing anything -+ send_zero_length(0, dev); -+ } else {//we have to receive a ZLP -+ //this will happen when the tx is complete, the pio_advance fcn will activate it for us -+ } -+ } -+ } -+ -+ req->req.complete(&ep->ep, &req->req); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int -+n9604_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) -+{ -+ struct n9604_request *req; -+ struct n9604_ep *ep; -+ struct n9604_udc *dev; -+ unsigned long flags; -+ int status; -+ -+ req = container_of(_req, struct n9604_request, req); -+ if (unlikely(!_req || !_req->complete -+ || !_req->buf || !list_empty(&req->queue))) -+ return -EINVAL; -+ ep = container_of(_ep, struct n9604_ep, ep); -+ if (unlikely(!_ep || (!ep->desc && ep->num != 0))) -+ return -EINVAL; -+ dev = ep->dev; -+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { -+ return -ESHUTDOWN; -+ } -+ if (ep->nuking) -+ return -ESHUTDOWN; -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ -+ ep->queue_reqs++; -+ ep->queue_active++; -+ -+ _req->status = -EINPROGRESS; -+ _req->actual = 0; -+ -+ /* for ep0 IN without premature status, zlp is required and -+ * writing EOP starts the status stage (OUT). -+ */ -+ if (ep->num == 0) { -+ if ((request_voodoo > _req->length) && !(_req->length % MAX_EP0_SIZE) && (_req->length != 0)) { -+ _req->zero = 1; -+ } -+ if (!request_voodoo && !ep->is_in) {//this is a zero length request -+ spin_unlock_irqrestore(&dev->lock, flags);//David -+ done(ep, req, 0);//this doesn't check if the list is empty (probably not an issue) -+ return 0; //shouldn't this be handled by the rx irq fcn, and passed to pio_advance -+ }//that may conflict with the voodoo stuff, maybe best to leave it -+ } -+ -+ /* kickstart this i/o queue? */ -+ status = 0; -+ if (list_empty(&ep->queue) && ep->is_in) { -+ status = write_fifo(ep, req); -+ if (status == -EBUSY) -+ ;//we should queue up the request then -+ else { -+ if (status != 0) { -+ if (status > 0) -+ status = 0; -+ req = 0; -+ } -+ } -+ } /* else pio or dma irq handler advances the queue. */ -+ -+ if (req != 0) { -+ list_add_tail(&req->queue, &ep->queue); -+ pio_irq_enable(ep); -+ } -+ -+ spin_unlock_irqrestore(&dev->lock, flags); -+ return status; -+} -+ -+/* dequeue ALL requests */ -+static void nuke(struct n9604_ep *ep, int status) -+{ -+ struct n9604_request *req; -+ -+ if (list_empty(&ep->queue)) -+ return; -+ ep->nuking = 1; -+ while (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, struct n9604_request, queue); -+ done(ep, req, status); -+ } -+ ep->nuking = 0; -+} -+ -+/* dequeue JUST ONE request */ -+static int n9604_dequeue(struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct n9604_request *req; -+ struct n9604_ep *ep; -+ struct n9604_udc *dev; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct n9604_ep, ep); -+ if (!_ep || !_req || (!ep->desc && ep->num != 0)) -+ return -EINVAL; -+ dev = ep->dev; -+ -+ if (!dev->driver) -+ return -ESHUTDOWN; -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ -+ /* make sure it's actually queued on this endpoint */ -+ list_for_each_entry (req, &ep->queue, queue) { -+ if (&req->req == _req) -+ break; -+ } -+ if (&req->req != _req) { -+ spin_unlock_irqrestore (&dev->lock, flags); -+ return -EINVAL; -+ } -+ -+ spin_unlock_irqrestore(&dev->lock, flags); -+ -+ return req ? 0 : -EOPNOTSUPP; -+} -+ -+static int n9604_clear_halt(struct usb_ep *_ep) { -+ struct n9604_ep *ep; -+ ep = container_of (_ep, struct n9604_ep, ep); -+ -+ write_9604(read_9604(ep->control) & ~EPC_STALL, ep->control); -+ pio_advance(ep); -+ return 0; -+} -+ -+static int n9604_set_halt(struct usb_ep *_ep, int value) { -+ struct n9604_ep *ep; -+ unsigned long flags; -+ int retval = 0; -+ -+ if (!_ep) { -+ retval = -ENODEV; goto exit; -+ } -+ ep = container_of (_ep, struct n9604_ep, ep); -+ -+ if (ep->num == 0) {//is this valid? -+ if (!value) { -+ retval = -EINVAL; goto exit; } -+ -+ /* don't change EPxSTATUS_EP_INVALID to READY */ -+ } else if (!ep->desc) { -+ retval = -EINVAL; goto exit; -+ } -+ -+ spin_lock_irqsave(&ep->dev->lock, flags); -+ if (!list_empty(&ep->queue)) -+ retval = -EAGAIN; -+ else if (!value) -+ n9604_clear_halt(_ep); -+ else { -+ write_9604(read_9604(ep->control) | EPC_STALL, ep->control); -+ } -+ spin_unlock_irqrestore(&ep->dev->lock, flags); -+exit: -+ return retval; -+} -+ -+static int n9604_fifo_status(struct usb_ep *_ep) {//not implemented -+ return -1; -+} -+ -+static void n9604_fifo_flush(struct usb_ep *_ep) {//not implemented -+ struct n9604_ep *ep; -+ ep = container_of (_ep, struct n9604_ep, ep); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_ep_ops n9604_ep_ops = { -+ .enable = n9604_ep_enable, -+ .disable = n9604_ep_disable, -+ -+ .alloc_request = n9604_alloc_request,//io request objects called struct usb_request -+ .free_request = n9604_free_request, -+ -+ .alloc_buffer = n9604_alloc_buffer, -+ .free_buffer = n9604_free_buffer, -+ -+ .queue = n9604_queue,//submit a struct usb_request object to an endpoint -+ .dequeue = n9604_dequeue, -+ -+ .set_halt = n9604_set_halt,//halts an endpoint -+ .fifo_status = n9604_fifo_status,//bytes in FIFO + data ready to go in FIFO -+ .fifo_flush = n9604_fifo_flush,//flush all the data, endpoint is probably been reconfigured -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int n9604_get_frame(struct usb_gadget *_gadget) -+{ -+ return -EOPNOTSUPP; -+} -+ -+static const struct usb_gadget_ops n9604_ops = { -+ .get_frame = n9604_get_frame, -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void udc_reinit (struct n9604_udc *dev) -+{ -+ static char *names [] = { "ep0", "ep1in", "ep2out", "ep3in", "ep4out", "ep5in", "ep6out" }; -+ unsigned i; -+ -+ INIT_LIST_HEAD (&dev->gadget.ep_list); -+ dev->gadget.ep0 = &dev->ep [0].ep; -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ dev->irqs = 0; -+ dev->configured = 0; -+ -+ //for (i = 0; i < 7; i++) { -+ for (i = 0; i < ARRAY_SIZE(names); i++) { -+ struct n9604_ep *ep = &dev->ep[i]; -+ ep->num = i; -+ ep->numActual = i; -+ ep->ep.name = names[i]; -+ ep->irqs = 0; -+ if (i) { -+ ep->fifo = (i * 4) + RXD0; //each FIFO address is 4 bytes away. TXD0 is the first -+ ep->control = ep->fifo - 1; -+ ep->status = ep->fifo + 1; -+ ep->command = ep->fifo + 2; -+ Flush(ep->command);//flush any data in the fifo//we don't care about the previous state -+ read_9604(ep->status); -+ ep->ep.maxpacket = MAX_FIFO_SIZE; -+ } else {//were are endpoint 0 -+ ep->fifo = ep->control = ep->status = ep->command = 0xff;//this should force an error -+ //we need to do this since we don't know if -+ //this is tx or rx -+ read_9604(TXS0); -+ Flush(TXC0); -+ Flush(RXC0);//we could potentially (probably) overwriting a pending setup packet -+ if (ep->stage)//if we get a setup packet before we have a chance to finish the reset we have a problem -+ read_9604(RXS0);//fix this by sending stalls or something -+ ep->stage = 0; -+ ep->ep.maxpacket = MAX_EP0_SIZE; -+ } -+ ep->is_in = i % 2; -+ ep->fifoNum = (i + ep->is_in) / 2;//ignored for endpoint 0 -+ ep->ep.ops = &n9604_ep_ops; -+ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); -+ ep->dev = dev; -+ INIT_LIST_HEAD (&ep->queue); -+ ep->nuking=0; -+ ep->queue_reqs = 0; -+ ep->queue_active = 0; -+ ep->packets = 0; -+ ep->desc = 0; -+ ep->irqs = 0; -+ } -+ -+ list_del_init (&dev->ep[0].ep.ep_list); -+ -+ write_9604(~WKUP_PNDUSB & ~WKUP_PNDUC & read_9604(WKUP), WKUP);//clear the bits, we've done a reset -+ write_9604(FAR_AD_EN, FAR);//enable the chip to answer requests//address 0 -+ dev->address = 0; -+ write_9604(0, EPC0);//clear the control register -+ write_9604(NFSR_NodeOperational, NFSR);//we're going for gold -+} -+ -+static void udc_reset(struct n9604_udc *dev) -+{ -+ //USBD_DISABLE_IRQ; This disables all interrupts sharing that line -+ write_9604(MCNTRL_SRST,MCNTRL);//software reset -- this also prevents pullup -+ write_9604(0x00, MAMSK); //disable interrupts -+} -+ -+ -+ -+static void udc_enable(struct n9604_udc *dev) -+{ -+ udc_reset(dev); //this is to prevent a pullup resistor -+ udc_reinit (dev); -+ -+ dev->gadget.speed = USB_SPEED_FULL; -+ -+ // enable ep0 interrupts -+ dev->ep[0].is_in = 0; -+ -+ write_9604(MAMSK_WARN | MAMSK_ALT | MAMSK_TX_EV | MAMSK_RX_EV | MAMSK_INTR, MAMSK);//for now we turn it all on, except frames & ULD & NAK -+ write_9604(ALTMSK_RESET, ALTMSK);//just turn on reset -+ write_9604(0x11, TXMSK); -+ write_9604(0x11, RXMSK); -+ write_9604(0x0, NAKMSK); -+ write_9604(0x0, FWMSK); -+ write_9604(MCNTRL_NAT | MCNTRL_INTOC_ActHigh, MCNTRL);//this activates the pull-up and turns on interrupts -+ USBD_ENABLE_IRQ; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* keeping it simple: -+ * - one bus driver, initted first; -+ * - one function driver, initted second -+ */ -+ -+static struct n9604_udc *the_controller; -+ -+/* when a driver is successfully registered, it will receive -+ * control requests including set_configuration(), which enables -+ * non-control requests. then usb traffic follows until a -+ * disconnect is reported. then a host may connect again, or -+ * the driver might get unbound. -+ */ -+int usb_gadget_register_driver(struct usb_gadget_driver *driver) -+{ -+ struct n9604_udc *dev = the_controller; -+ int retval; -+ -+ if (!driver -+ || driver->speed != USB_SPEED_FULL -+ || !driver->bind -+ || !driver->unbind -+ || !driver->disconnect -+ || !driver->setup) -+ return -EINVAL; -+ if (!dev) -+ return -ENODEV; -+ if (dev->driver) -+ return -EBUSY; -+ -+ /* hook up the driver */ -+ dev->driver = driver; -+ retval = driver->bind(&dev->gadget); -+ if (retval) { -+ dev->driver = 0; -+ return retval; -+ } -+ -+ /* then enable host detection and ep0; and we're ready -+ * for set_configuration as well as eventual disconnect. -+ */ -+ udc_enable(dev); -+ -+ return 0; -+} -+EXPORT_SYMBOL(usb_gadget_register_driver); -+ -+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -+{ -+ struct n9604_udc *dev = the_controller; -+ unsigned long flags; -+ int i; -+ -+ if (!dev) -+ return -ENODEV; -+ if (!driver || driver != dev->driver) -+ return -EINVAL; -+ -+ spin_lock_irqsave(&dev->lock, flags); -+ dev->driver = 0; -+ -+ udc_reset(dev);//reset & diable irqs -+ for (i = 0; i < ARRAY_SIZE(dev->ep); i++) -+ nuke(&dev->ep [i], -ESHUTDOWN); -+ spin_unlock_irqrestore(&dev->lock, flags); -+ -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN) -+ driver->disconnect(&dev->gadget); -+ driver->unbind(&dev->gadget); -+ -+ return 0; -+} -+EXPORT_SYMBOL(usb_gadget_unregister_driver); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+inline u8 tx_ev_irq(struct n9604_udc *dev) { -+ u8 mask; -+ -+ mask = read_9604(TXEV) & read_9604(TXMSK); -+ -+ if (mask & TXEV_FIFO0) { -+ write_9604(0, EPC0);//make sure we are not stalled, & not using the default address -+ read_9604(TXS0);//should really check for error conditions -+ dev->ep[0].irqs++; -+ pio_advance(&dev->ep[0]); -+ } -+ if (mask & TXEV_FIFO1) { -+ read_9604(TXS1); -+ dev->ep[1].irqs++; -+ pio_advance(&dev->ep[1]); -+ } -+ if (mask & TXEV_FIFO2) { -+ read_9604(TXS2); -+ dev->ep[3].irqs++; -+ pio_advance(&dev->ep[3]); -+ } -+ if (mask & TXEV_FIFO3) { -+ read_9604(TXS3); -+ dev->ep[5].irqs++; -+ pio_advance(&dev->ep[5]); -+ } -+ return mask; -+} -+ -+static void my_req_complete(struct usb_ep *_ep, struct usb_request *req) {//this was for the setup packet, but I guess I could use it for anything -+ n9604_free_buffer(_ep, req->buf, req->dma, req->length); -+ n9604_free_request(_ep, req); -+} -+ -+inline void send_dummy_packet(int endpoint, struct n9604_udc *dev, int length) { -+ struct usb_request *my_req; -+ my_req = n9604_alloc_request(&dev->ep[endpoint].ep, GFP_ATOMIC); -+ my_req->length = length; -+ my_req->buf = n9604_alloc_buffer(&dev->ep[endpoint].ep, length, &my_req->dma, GFP_ATOMIC); -+ my_req->complete = my_req_complete; -+ n9604_queue(&dev->ep[endpoint].ep, my_req, GFP_ATOMIC); -+} -+ -+inline void send_zero_length(int endpoint, struct n9604_udc *dev) { -+ send_dummy_packet(endpoint, dev, 0); -+} -+ -+inline void rx_ev_irq(struct n9604_udc *dev) { -+ u8 mask; -+ struct n9604_ep *ep; -+ -+ mask = read_9604(RXEV) & read_9604(RXMSK); -+ -+ if (mask & RXEV_FIFO0) { -+ static int read_mode = 0; -+ u8 rxs_mask = read_9604(RXS0); -+ ep = &dev->ep[0]; -+ ep->irqs++; -+ if (rxs_mask & RXS_SETUP) { -+ struct usb_ctrlrequest ctrl; -+ ep->packets++; -+ write_9604(0x40, ALTMSK);//someone is talking to us. Make sure we can be reset if we lose this communication -+ ep->stage = 1; -+ rxs_mask = read_9604(RXS0);//2nd read (1st one is for zero length packet) -+ ctrl.bRequestType = read_9604(RXD0); -+ ctrl.bRequest = read_9604(RXD0); -+ ctrl.wValue = read_9604(RXD0) + (read_9604(RXD0) << 8); -+ ctrl.wIndex = read_9604(RXD0) + (read_9604(RXD0) << 8); -+ ctrl.wLength = read_9604(RXD0) + (read_9604(RXD0) << 8); -+ ep->toggle = 1; -+ request_voodoo = ctrl.wLength; -+ if (ctrl.bRequestType & 0x80) {//This is an IN transaction -+ ep->is_in = 1;//David: is this correct for both cases//check with n9604_queue -+ read_mode = 0; -+ if (ctrl.wLength) {//should be followed by ZLP out packet -+ } else {//host expects ZLP out packet -+ ep->stage = 2; -+ } -+ } else {//This is an out transaction -+ if (ctrl.wLength) { -+ ep->is_in = 0; -+ read_mode = 1; -+ } else {//host expects ZLP in packet -+ read_mode = 0; -+ ep->stage = 2; -+ ep->is_in = 1; -+ } -+ } -+ switch (ctrl.bRequest) { -+ case USB_REQ_SET_ADDRESS: -+ write_9604(EPC_DEF, EPC0);//we still want to respond to the default address -+ write_9604(((dev->address = (ctrl.wValue & FAR_AD_MASK))) | FAR_AD_EN, FAR); -+ send_zero_length(0, dev); -+ dev->configured = 1;//we can send longer packets now :) -+ read_9604(ALTEV); -+ write_9604(ALTMSK_RESET, ALTMSK);//we also listen to reset requests too -+ break; -+ case USB_REQ_CLEAR_FEATURE: -+ if (ctrl.wValue == 0 && ctrl.bRequestType == 2) {//endpoint halt -+ int i; -+ for (i = 0; i < ARRAY_SIZE(dev->ep); i++) -+ if ((ctrl.wIndex & 0xF) == dev->ep[i].numActual) -+ n9604_clear_halt(&dev->ep[i].ep); -+ send_zero_length(0, dev); -+ break; -+ } -+ case USB_REQ_SET_DESCRIPTOR: -+ case USB_REQ_SYNCH_FRAME: -+ case USB_REQ_GET_STATUS: -+ case USB_REQ_SET_FEATURE: -+ case USB_REQ_SET_CONFIGURATION: -+ case USB_REQ_GET_DESCRIPTOR: -+ case USB_REQ_GET_CONFIGURATION: -+ case USB_REQ_SET_INTERFACE: -+ case USB_REQ_GET_INTERFACE: -+ default: -+ if (dev->driver->setup(&dev->gadget, &ctrl) < 0)//there was an error -+ if (((ctrl.bRequestType & 0x80) && ctrl.wLength) || (!(ctrl.bRequestType & 0x80) && !ctrl.wLength)) -+ send_zero_length(0, dev); -+ }//crtl.bRequest -+ }//setup -+ else if (read_mode) -+ pio_advance(ep); -+ else { -+ ep->stage = 0; -+ ep->packets++; -+ } -+ }//fifo 0 -+ if (mask & RXEV_FIFO1) { -+ ep = &dev->ep[2]; -+ pio_advance(ep); -+ ep->irqs++; -+ } -+ if (mask & RXEV_FIFO2) { -+ ep = &dev->ep[4]; -+ pio_advance(ep); -+ ep->irqs++; -+ } -+ if (mask & RXEV_FIFO3) { -+ ep = &dev->ep[6]; -+ pio_advance(ep); -+ ep->irqs++; -+ } -+} -+ -+inline void alt_ev_irq(struct n9604_udc *dev) { -+ u8 mask; -+ -+ mask = read_9604(ALTEV) & read_9604(ALTMSK); -+ -+ if (mask & ALTEV_EOP); -+ if (mask & ALTEV_SD3); -+ if (mask & ALTEV_SD5); -+ if (mask & ALTEV_RESET) { -+ int i; -+ udelay(1200);//no idea why this is needed, but it makes things work -+ write_9604(0x0, FAR);//lets not respond to any packets until we are ready -+ write_9604(NFSR_NodeReset, NFSR); -+ dev->driver->disconnect(&dev->gadget); -+ for (i = 0; i < ARRAY_SIZE(dev->ep); i++) -+ nuke(&dev->ep [i], -ESHUTDOWN);//this should be handled above by disconnect -+ write_9604(0x00, ALTMSK);//make sure reset is turned off, or we will constantly be interrupted -+ write_9604(0x11, TXMSK); -+ write_9604(0x11, RXMSK); -+ udc_reinit(dev); -+ dev->gadget.speed = USB_SPEED_FULL; -+ dev->ep[0].is_in = 0; -+ } -+ if (mask & ALTEV_RESUME); //write_9604(NFSR_NodeOperational, NFSR); -+ if (mask & ALTEV_WKUP);//we don't really sleep -+ if (mask & ALTEV_DMA); -+} -+ -+static void n9604_irq(int irq, void *_dev, struct pt_regs *r) { -+ struct n9604_udc *dev = _dev; -+ u8 mask; -+ -+ mask = read_9604(MAEV) & read_9604(MAMSK); -+ if (!mask) -+ return; -+ -+ if (mask & MAEV_ALT) { -+ alt_ev_irq(dev); -+ mask = read_9604(MAEV) & read_9604(MAMSK);//force a re-read of the current pending interrupts -+ } -+ if (mask & MAEV_TX_EV) -+ tx_ev_irq(dev); -+ if (mask & MAEV_RX_EV) -+ rx_ev_irq(dev); -+ dev->irqs++; -+ return; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int __init init (void) -+{ -+ struct n9604_udc *dev; -+ int ret; -+ u8 * addr; -+ -+ if (the_controller) -+ return -EBUSY; -+ -+ addr = ioremap(USBN9604_PHYS, 0x2);//ioremap will bump this to 1 page size -+ if (!addr) { -+ ERROR(dev, KERN_ERR "Unable to remap address\n"); -+ return -EINVAL; -+ } -+ -+ USBN9604_Offset = addr; -+ -+ if ((read_9604(RID) & 0xF) != 0x2) { //0x2 is the identifier for 9603/4 -+ iounmap(addr); -+ return -ENODEV; -+ } -+ -+ /* alloc, and start init */ -+ dev = kmalloc(sizeof *dev, SLAB_KERNEL); -+ if (dev == NULL){ -+ WARN(dev, "No memory"); -+ iounmap(addr); -+ return -ENOMEM; -+ } -+ memset(dev, 0, sizeof *dev); -+ spin_lock_init(&dev->lock); -+ dev->gadget.ops = &n9604_ops; -+ dev->gadget.is_dualspeed = 0; -+ -+ /* the "gadget" abstracts/virtualizes the controller */ -+ dev->gadget.dev.bus_id = "gadget"; -+ dev->gadget.name = driver_name; -+ -+ /* initialize the hardware */ -+ -+ udc_reset(dev); -+ -+ write_9604(CCONF_CODIS | 11, CCONF); -+ -+ udc_reinit(dev);//this is necessary as it sets up the epx functions -+ -+ the_controller = dev; -+ -+ if ((ret=request_irq(IRQ_GPIOC, n9604_irq, SA_SHIRQ, driver_name,dev))) { -+ WARN(dev, "Can't get IRQ\n"); -+ iounmap(addr); -+ return ret; -+ } -+ -+ return 0; -+} -+module_init (init); -+ -+static void __exit cleanup (void) -+{ -+ struct n9604_udc *dev = the_controller; -+ -+ //first kill the interrupts -+ udc_reset(dev); -+ free_irq(IRQ_GPIOC, dev); -+ -+ /* start with the driver above us */ -+ if (dev->driver) { -+ /* should have been done already by driver model core */ -+ WARN(dev, "Warning: Driver '%s' is still registered\n", -+ dev->driver->driver.name); -+ usb_gadget_unregister_driver(dev->driver); -+ } -+ kfree(dev); -+ iounmap(USBN9604_Offset); -+ the_controller = 0; -+ -+} -+module_exit (cleanup); -+ -+MODULE_PARM_DESC (delayTime, "Delays after reads and writes to the USB chip"); -+MODULE_PARM (delayTime, "i"); -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/n9604.h kernel/drivers/usb/gadget/n9604.h ---- /tmp/kernel/drivers/usb/gadget/n9604.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/n9604.h 2005-04-22 17:53:19.463534794 +0200 -@@ -0,0 +1,112 @@ -+/* -+ * National 9604 USB device controller driver -+ * -+ * Copyright 2003 Technical Solutions Inc. -+ * -+ * ported from: -+ * -+ * Toshiba TC86C001 ("Goku-S") USB Device Controller driver -+ * -+ * Copyright (C) 2000-2002 Lineo -+ * by Stuart Lynne, Tom Rushworth, and Bruce Balden -+ * Copyright (C) 2002 Toshiba Corporation -+ * Copyright (C) 2003 MontaVista Software (source@mvista.com) -+ * -+ * This file is licensed under the terms of the GNU General Public -+ * License version 2. This program is licensed "as is" without any -+ * warranty of any kind, whether express or implied. -+ */ -+ -+#define MAX_FIFO_SIZE 64 -+#define MAX_EP0_SIZE 8 -+ -+struct n9604_ep { -+ struct usb_ep ep; -+ struct n9604_udc *dev; -+ unsigned long irqs; -+ int acct_req_lengths[4]; -+ int acct_req_dir[4];//direction -+ unsigned long queue_reqs;//how many times has n9604_queue been called -+ unsigned long queue_active;//how many current requests -+ unsigned long packets;//counter of raw packets -+ unsigned num:4, -+ numActual:4, -+ fifoNum:2, -+ is_in:1, -+ stage:2,//for ep0, 0 = unused, 1 = got setup, 2 = done transfer/ready to send/receive ZLP -+ toggle:1, -+ nuking:1;//are we killing on off this endpoint//only used for ep0 to help with stages -+ /* analogous to a host-side qh */ -+ struct list_head queue; -+ const struct usb_endpoint_descriptor *desc; -+ -+ u8 control; -+ u8 fifo; -+ u8 status; -+ u8 command; -+}; -+ -+struct n9604_request { -+ struct usb_request req; -+ struct list_head queue; -+ int complete;//this is added for tx requests -+ //if set the entire request has been written to the fifo, just waiting for confirmation -+ //from the interrupt that it has been sent -+ -+ unsigned mapped:1; -+}; -+ -+struct n9604_udc { -+ struct usb_gadget gadget; -+ spinlock_t lock; -+ struct n9604_ep ep[7]; -+ struct usb_gadget_driver *driver; -+ int configured; -+ -+ u8 address; -+ -+ /* statistics... */ -+ unsigned long irqs; -+}; -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define xprintk(dev,level,fmt,args...) \ -+ printk(level "%s %s: " fmt , driver_name , \ -+ "S2410 gadget" , ## args) -+ -+#define ERROR(dev,fmt,args...) \ -+ xprintk(dev , KERN_ERR , fmt , ## args) -+#define WARN(dev,fmt,args...) \ -+ xprintk(dev , KERN_WARNING , fmt , ## args) -+#define INFO(dev,fmt,args...) \ -+ xprintk(dev , KERN_INFO , fmt , ## args) -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* 2.5 stuff that's sometimes missing in 2.4 */ -+ -+#ifndef container_of -+#define container_of list_entry -+#endif -+ -+#ifndef likely -+#define likely(x) (x) -+#define unlikely(x) (x) -+#endif -+ -+#ifndef BUG_ON -+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) -+#endif -+ -+#ifndef WARN_ON -+#define WARN_ON(x) do { } while (0) -+#endif -+ -+#ifndef IRQ_NONE -+typedef void irqreturn_t; -+#define IRQ_NONE -+#define IRQ_HANDLED -+#define IRQ_RETVAL(x) -+#endif -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/n9604regs.h kernel/drivers/usb/gadget/n9604regs.h ---- /tmp/kernel/drivers/usb/gadget/n9604regs.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/n9604regs.h 2005-04-22 17:53:19.466534306 +0200 -@@ -0,0 +1,248 @@ -+/* National 9604 registers */ -+ -+#define USBN9604_PHYS 0x08000000 -+ -+extern u8 * USBN9604_Offset; -+ -+static u8 last_address = 255;//an invalid address -+ -+inline u8 read_9604(u8 addr) { -+ u8 tmp; -+ if (addr != last_address) { -+ outb(addr, USBN9604_Offset + 1); -+ last_address = addr; -+ } -+ tmp = inb(USBN9604_Offset); -+ return tmp; -+} -+ -+inline void write_9604(u8 value, u8 addr) { -+ if (addr != last_address) { -+ outb(addr, USBN9604_Offset + 1); -+ last_address = addr; -+ } -+ outb(value, USBN9604_Offset); -+} -+ -+ -+ -+#define MCNTRL 0x00 -+#define CCONF 0x01 -+ -+#define RID 0x03 -+#define FAR 0x04 -+#define NFSR 0x05 -+#define MAEV 0x06 -+#define MAMSK 0x07 -+#define ALTEV 0x08 -+#define ALTMSK 0x09 -+#define TXEV 0x0A -+#define TXMSK 0x0B -+#define RXEV 0x0C -+#define RXMSK 0x0D -+#define NAKEV 0x0E -+#define NAKMSK 0x0F -+#define FWEV 0x10 -+#define FWMSK 0x11 -+#define FNH 0x12 -+#define FNL 0x13 -+#define DMACNTRL 0x14 -+#define DMAEV 0x15 -+#define DMAMSK 0x16 -+#define MIR 0x17 -+#define DMACNT 0x18 -+#define DMAERR 0x19 -+ -+#define WKUP 0x1B -+ -+ -+ -+ -+#define EPC0 0x20 -+#define TXD0 0x21 -+#define TXS0 0x22 -+#define TXC0 0x23 -+ -+#define RXD0 0x25 -+#define RXS0 0x26 -+#define RXC0 0x27 -+#define EPC1 0x28 -+#define TXD1 0x29 -+#define TXS1 0x2A -+#define TXC1 0x2B -+#define EPC2 0x2C -+#define RXD1 0x2D -+#define RXS1 0x2E -+#define RXC1 0x2F -+#define EPC3 0x30 -+#define TXD2 0x31 -+#define TXS2 0x32 -+#define TXC2 0x33 -+#define EPC4 0x34 -+#define RXD2 0x35 -+#define RXS2 0x36 -+#define RXC2 0x37 -+#define EPC5 0x38 -+#define TXD3 0x39 -+#define TXS3 0x3A -+#define TXC3 0x3B -+#define EPC6 0x3C -+#define RXD3 0x3D -+#define RXS3 0x3E -+#define RXC3 0x3F -+ -+ -+/* MCNTRL values */ -+#define MCNTRL_SRST (1 << 0) -+#define MCNTRL_VGE (1 << 2) -+#define MCNTRL_NAT (1 << 3) -+#define MCNTRL_INTOC_MASK (3 << 6) -+#define MCNTRL_INTOC_DISABLE 0 -+#define MCNTRL_INTOC_ActLowOpen (1 << 6) -+#define MCNTRL_INTOC_ActHigh (2 << 6) -+#define MCNTRL_INTOC_ActLowPP (3 << 6) -+ -+/* CCONF values */ -+#define CCONF_CLKDIV_MASK 0x0F -+#define CCONF_CODIS (1 << 7) -+ -+/* FAR values */ -+#define FAR_AD_MASK 0x7F -+#define FAR_AD_EN 0x80 -+ -+/* NFSR values */ -+#define NFSR_NodeReset 0x0 -+#define NFSR_NodeResume 0x1 -+#define NFSR_NodeOperational 0x2 -+#define NFSR_NodeSuspend 0x3 -+ -+/* MAEV values */ -+#define MAEV_WARN (1 << 0) -+#define MAEV_ALT (1 << 1) -+#define MAEV_TX_EV (1 << 2) -+#define MAEV_FRAME (1 << 3) -+#define MAEV_NAK (1 << 4) -+#define MAEV_ULD (1 << 5) -+#define MAEV_RX_EV (1 << 6) -+#define MAEV_INTR (1 << 7) -+ -+/* MAMSK values */ -+#define MAMSK_WARN (1 << 0) -+#define MAMSK_ALT (1 << 1) -+#define MAMSK_TX_EV (1 << 2) -+#define MAMSK_FRAME (1 << 3) -+#define MAMSK_NAK (1 << 4) -+#define MAMSK_ULD (1 << 5) -+#define MAMSK_RX_EV (1 << 6) -+#define MAMSK_INTR (1 << 7) -+ -+/* ALTEV values */ -+ -+#define ALTEV_WKUP (1 << 1) -+#define ALTEV_DMA (1 << 2) -+#define ALTEV_EOP (1 << 3) -+#define ALTEV_SD3 (1 << 4) -+#define ALTEV_SD5 (1 << 5) -+#define ALTEV_RESET (1 << 6) -+#define ALTEV_RESUME (1 << 7) -+ -+/* ALTMSK values */ -+ -+#define ALTMSK_WKUP (1 << 1) -+#define ALTMSK_DMA (1 << 2) -+#define ALTMSK_EOP (1 << 3) -+#define ALTMSK_SD3 (1 << 4) -+#define ALTMSK_SD5 (1 << 5) -+#define ALTMSK_RESET (1 << 6) -+#define ALTMSK_RESUME (1 << 7) -+ -+/* NAKEV values */ -+ -+#define NAKEV_TXFIFO0 (1 << 0) -+#define NAKEV_TXFIFO1 (1 << 1) -+#define NAKEV_TXFIFO2 (1 << 2) -+#define NAKEV_TXFIFO3 (1 << 3) -+#define NAKEV_RXFIFO0 (1 << 4) -+#define NAKEV_RXFIFO1 (1 << 5) -+#define NAKEV_RXFIFO2 (1 << 6) -+#define NAKEV_RXFIFO3 (1 << 7) -+ -+ -+/* WKUP values */ -+#define WKUP_PNDUSB (1 << 0) -+#define WKUP_PNDUC (1 << 1) -+#define WKUP_ENUSB (1 << 2) -+#define WKUP_ENUC (1 << 3) -+#define WKUP_WKMODE (1 << 5) -+#define WKUP_HOS (1 << 6) -+#define WKUP_FHT (1 << 7) -+ -+/* EPC values */ -+ -+#define EPC_EP_MASK 0x0F //EP0 == 0 -+#define EPC_EP_EN (1 << 4)//not EP0 -+#define EPC_ISO (1 << 5)//not EP0 -+#define EPC_DEF (1 << 6)//EP0 only -+#define EPC_STALL (1 << 7) -+ -+/* TXS values */ -+ -+#define TXS_TCOUNT_MASK 0x1F -+#define TXS_TX_DONE (1 << 5) -+#define TXS_ACK_STAT (1 << 6) -+#define TXS_TX_URUN (1 << 7) -+ -+/* TXC values */ -+ -+#define TXC_TX_EN (1 << 0) -+#define TXC_LAST (1 << 1)//not for endpoint 0 -+#define TXC_TOGGLE (1 << 2)//sets DATA1 when set -+#define TXC_FLUSH (1 << 3) -+#define TXC_IGN_IN (1 << 4)//only endpoint 0 -+#define TXC_RFF (1 << 4)//not for endpoint 0 -+#define TXC_TFWL0 (1 << 5)//" -+#define TXC_TFWL1 (1 << 6)//" -+#define TXC_IGN_ISOMSK (1 << 7)//" -+ -+/* TXEV values */ -+ -+#define TXEV_FIFO0 (1 << 0) -+#define TXEV_FIFO1 (1 << 1) -+#define TXEV_FIFO2 (1 << 2) -+#define TXEV_FIFO3 (1 << 3) -+#define TXEV_UDRRN0 (1 << 4) -+#define TXEV_UDRRN1 (1 << 5) -+#define TXEV_UDRRN2 (1 << 6) -+#define TXEV_UDRRN3 (1 << 7) -+ -+ -+/* RXEV values */ -+ -+#define RXEV_FIFO0 (1 << 0) -+#define RXEV_FIFO1 (1 << 1) -+#define RXEV_FIFO2 (1 << 2) -+#define RXEV_FIFO3 (1 << 3) -+#define RXEV_OVRRN0 (1 << 4) -+#define RXEV_OVRRN1 (1 << 5) -+#define RXEV_OVRRN2 (1 << 6) -+#define RXEV_OVRRN3 (1 << 7) -+ -+/* RXC values */ -+ -+#define RXC_RX_EN (1 << 0) -+#define RXC_IGN_OUT (1 << 1) -+#define RXC_IGN_SETUP (1 << 2) -+#define RXC_FLUSH (1 << 3) -+#define RXC_RFWL0 (1 << 5) -+#define RXC_RFWL1 (1 << 6) -+ -+/* RXS values */ -+ -+#define RXS_RCOUNTMASK 0xF -+#define RXS_RX_LAST (1 << 4) -+#define RXS_TOGGLE (1 << 5) -+#define RXS_SETUP (1 << 6) -+#define RXS_RX_ERR (1 << 7) -+ -+ -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/ndis.h kernel/drivers/usb/gadget/ndis.h ---- /tmp/kernel/drivers/usb/gadget/ndis.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/ndis.h 2005-04-22 17:53:19.469533817 +0200 -@@ -0,0 +1,217 @@ -+/* -+ * ndis.h -+ * -+ * ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de> -+ * -+ * Thanks to the cygwin development team, -+ * espacially to Casper S. Hornstrup <chorns@users.sourceforge.net> -+ * -+ * THIS SOFTWARE IS NOT COPYRIGHTED -+ * -+ * This source code is offered for use in the public domain. You may -+ * use, modify or distribute it freely. -+ * -+ * This code is distributed in the hope that it will be useful but -+ * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY -+ * DISCLAIMED. This includes but is not limited to warranties of -+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -+ * -+ */ -+ -+#ifndef _LINUX_NDIS_H -+#define _LINUX_NDIS_H -+ -+ -+#define NDIS_STATUS_MULTICAST_FULL 0xC0010009 -+#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A -+#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B -+ -+enum NDIS_DEVICE_POWER_STATE { -+ NdisDeviceStateUnspecified = 0, -+ NdisDeviceStateD0, -+ NdisDeviceStateD1, -+ NdisDeviceStateD2, -+ NdisDeviceStateD3, -+ NdisDeviceStateMaximum -+}; -+ -+struct NDIS_PM_WAKE_UP_CAPABILITIES { -+ enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp; -+ enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp; -+ enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp; -+}; -+ -+/* NDIS_PNP_CAPABILITIES.Flags constants */ -+#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001 -+#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002 -+#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004 -+ -+struct NDIS_PNP_CAPABILITIES { -+ u32 Flags; -+ struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities; -+}; -+ -+struct NDIS_PM_PACKET_PATTERN { -+ u32 Priority; -+ u32 Reserved; -+ u32 MaskSize; -+ u32 PatternOffset; -+ u32 PatternSize; -+ u32 PatternFlags; -+}; -+ -+ -+/* Required Object IDs (OIDs) */ -+#define OID_GEN_SUPPORTED_LIST 0x00010101 -+#define OID_GEN_HARDWARE_STATUS 0x00010102 -+#define OID_GEN_MEDIA_SUPPORTED 0x00010103 -+#define OID_GEN_MEDIA_IN_USE 0x00010104 -+#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105 -+#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106 -+#define OID_GEN_LINK_SPEED 0x00010107 -+#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108 -+#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109 -+#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A -+#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B -+#define OID_GEN_VENDOR_ID 0x0001010C -+#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D -+#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E -+#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F -+#define OID_GEN_DRIVER_VERSION 0x00010110 -+#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111 -+#define OID_GEN_PROTOCOL_OPTIONS 0x00010112 -+#define OID_GEN_MAC_OPTIONS 0x00010113 -+#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114 -+#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115 -+#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116 -+#define OID_GEN_SUPPORTED_GUIDS 0x00010117 -+#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118 -+#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119 -+#define OID_GEN_MACHINE_NAME 0x0001021A -+#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B -+#define OID_GEN_VLAN_ID 0x0001021C -+ -+/* Optional OIDs */ -+#define OID_GEN_MEDIA_CAPABILITIES 0x00010201 -+#define OID_GEN_PHYSICAL_MEDIUM 0x00010202 -+ -+/* Required statistics OIDs */ -+#define OID_GEN_XMIT_OK 0x00020101 -+#define OID_GEN_RCV_OK 0x00020102 -+#define OID_GEN_XMIT_ERROR 0x00020103 -+#define OID_GEN_RCV_ERROR 0x00020104 -+#define OID_GEN_RCV_NO_BUFFER 0x00020105 -+ -+/* Optional statistics OIDs */ -+#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201 -+#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202 -+#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203 -+#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204 -+#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205 -+#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206 -+#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207 -+#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208 -+#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209 -+#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A -+#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B -+#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C -+#define OID_GEN_RCV_CRC_ERROR 0x0002020D -+#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E -+#define OID_GEN_GET_TIME_CAPS 0x0002020F -+#define OID_GEN_GET_NETCARD_TIME 0x00020210 -+#define OID_GEN_NETCARD_LOAD 0x00020211 -+#define OID_GEN_DEVICE_PROFILE 0x00020212 -+#define OID_GEN_INIT_TIME_MS 0x00020213 -+#define OID_GEN_RESET_COUNTS 0x00020214 -+#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215 -+#define OID_GEN_FRIENDLY_NAME 0x00020216 -+#define OID_GEN_MINIPORT_INFO 0x00020217 -+#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218 -+ -+/* IEEE 802.3 (Ethernet) OIDs */ -+#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001 -+ -+#define OID_802_3_PERMANENT_ADDRESS 0x01010101 -+#define OID_802_3_CURRENT_ADDRESS 0x01010102 -+#define OID_802_3_MULTICAST_LIST 0x01010103 -+#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104 -+#define OID_802_3_MAC_OPTIONS 0x01010105 -+#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101 -+#define OID_802_3_XMIT_ONE_COLLISION 0x01020102 -+#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103 -+#define OID_802_3_XMIT_DEFERRED 0x01020201 -+#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202 -+#define OID_802_3_RCV_OVERRUN 0x01020203 -+#define OID_802_3_XMIT_UNDERRUN 0x01020204 -+#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205 -+#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206 -+#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207 -+ -+/* OID_GEN_MINIPORT_INFO constants */ -+#define NDIS_MINIPORT_BUS_MASTER 0x00000001 -+#define NDIS_MINIPORT_WDM_DRIVER 0x00000002 -+#define NDIS_MINIPORT_SG_LIST 0x00000004 -+#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008 -+#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010 -+#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020 -+#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040 -+#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080 -+#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100 -+#define NDIS_MINIPORT_IS_NDIS_5 0x00000200 -+#define NDIS_MINIPORT_IS_CO 0x00000400 -+#define NDIS_MINIPORT_DESERIALIZE 0x00000800 -+#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000 -+#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000 -+#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000 -+#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000 -+#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000 -+#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000 -+#define NDIS_MINIPORT_HIDDEN 0x00040000 -+#define NDIS_MINIPORT_SWENUM 0x00080000 -+#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000 -+#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000 -+#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000 -+#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000 -+#define NDIS_MINIPORT_64BITS_DMA 0x01000000 -+ -+#define NDIS_MEDIUM_802_3 0x00000000 -+#define NDIS_MEDIUM_802_5 0x00000001 -+#define NDIS_MEDIUM_FDDI 0x00000002 -+#define NDIS_MEDIUM_WAN 0x00000003 -+#define NDIS_MEDIUM_LOCAL_TALK 0x00000004 -+#define NDIS_MEDIUM_DIX 0x00000005 -+#define NDIS_MEDIUM_ARCENT_RAW 0x00000006 -+#define NDIS_MEDIUM_ARCENT_878_2 0x00000007 -+#define NDIS_MEDIUM_ATM 0x00000008 -+#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009 -+#define NDIS_MEDIUM_IRDA 0x0000000A -+#define NDIS_MEDIUM_BPC 0x0000000B -+#define NDIS_MEDIUM_CO_WAN 0x0000000C -+#define NDIS_MEDIUM_1394 0x0000000D -+ -+#define NDIS_PACKET_TYPE_DIRECTED 0x00000001 -+#define NDIS_PACKET_TYPE_MULTICAST 0x00000002 -+#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004 -+#define NDIS_PACKET_TYPE_BROADCAST 0x00000008 -+#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010 -+#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020 -+#define NDIS_PACKET_TYPE_SMT 0x00000040 -+#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080 -+#define NDIS_PACKET_TYPE_GROUP 0x00000100 -+#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200 -+#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400 -+#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800 -+ -+#define NDIS_MEDIA_STATE_CONNECTED 0x00000000 -+#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001 -+ -+#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001 -+#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002 -+#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004 -+#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008 -+#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010 -+#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020 -+#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040 -+#define NDIS_MAC_OPTION_RESERVED 0x80000000 -+ -+#endif /* _LINUX_NDIS_H */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/net2280.c kernel/drivers/usb/gadget/net2280.c ---- /tmp/kernel/drivers/usb/gadget/net2280.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/net2280.c 2005-04-22 17:53:19.478532352 +0200 -@@ -0,0 +1,2918 @@ -+/* -+ * Driver for the NetChip 2280 USB device controller. -+ * Specs and errata are available from <http://www.netchip.com>. -+ * -+ * NetChip Technology Inc. supported the development of this driver. -+ * -+ * -+ * CODE STATUS HIGHLIGHTS -+ * -+ * This driver should work well with most "gadget" drivers, including -+ * the File Storage, Serial, and Ethernet/RNDIS gadget drivers -+ * as well as Gadget Zero and Gadgetfs. -+ * -+ * DMA is enabled by default. Drivers using transfer queues might use -+ * DMA chaining to remove IRQ latencies between transfers. (Except when -+ * short OUT transfers happen.) Drivers can use the req->no_interrupt -+ * hint to completely eliminate some IRQs, if a later IRQ is guaranteed -+ * and DMA chaining is enabled. -+ * -+ * Note that almost all the errata workarounds here are only needed for -+ * rev1 chips. Rev1a silicon (0110) fixes almost all of them. -+ */ -+ -+/* -+ * Copyright (C) 2003 David Brownell -+ * Copyright (C) 2003 NetChip Technologies -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#undef DEBUG /* messages on error and most fault paths */ -+#undef VERBOSE /* extra debug messages (success too) */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/pci.h> -+#include <linux/kernel.h> -+#include <linux/delay.h> -+#include <linux/ioport.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/smp_lock.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include <asm/byteorder.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/unaligned.h> -+ -+ -+#define DRIVER_DESC "NetChip 2280 USB Peripheral Controller" -+#define DRIVER_VERSION "2004 Jan 14" -+ -+#define DMA_ADDR_INVALID (~(dma_addr_t)0) -+#define EP_DONTUSE 13 /* nonzero */ -+ -+#define USE_RDK_LEDS /* GPIO pins control three LEDs */ -+ -+ -+static const char driver_name [] = "net2280"; -+static const char driver_desc [] = DRIVER_DESC; -+ -+static const char ep0name [] = "ep0"; -+static const char *ep_name [] = { -+ ep0name, -+ "ep-a", "ep-b", "ep-c", "ep-d", -+ "ep-e", "ep-f", -+}; -+ -+/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) -+ * use_dma_chaining -- dma descriptor queueing gives even more irq reduction -+ * -+ * The net2280 DMA engines are not tightly integrated with their FIFOs; -+ * not all cases are (yet) handled well in this driver or the silicon. -+ * Some gadget drivers work better with the dma support here than others. -+ * These two parameters let you use PIO or more aggressive DMA. -+ */ -+static int use_dma = 1; -+static int use_dma_chaining = 0; -+ -+MODULE_PARM (use_dma, "i"); -+MODULE_PARM_DESC (use_dma, "true to use dma controllers"); -+ -+MODULE_PARM (use_dma_chaining, "i"); -+MODULE_PARM_DESC (use_dma_chaining, "true to use dma descriptor queues"); -+ -+ -+/* mode 0 == ep-{a,b,c,d} 1K fifo each -+ * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable -+ * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable -+ */ -+static ushort fifo_mode = 0; -+ -+MODULE_PARM (fifo_mode, "h"); -+MODULE_PARM_DESC (fifo_mode, "net2280 fifo mode"); -+ -+ -+#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out") -+ -+#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG) -+static char *type_string (u8 bmAttributes) -+{ -+ switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { -+ case USB_ENDPOINT_XFER_BULK: return "bulk"; -+ case USB_ENDPOINT_XFER_ISOC: return "iso"; -+ case USB_ENDPOINT_XFER_INT: return "intr"; -+ }; -+ return "control"; -+} -+#endif -+ -+#include "net2280.h" -+ -+#define valid_bit __constant_cpu_to_le32 (1 << VALID_BIT) -+#define dma_done_ie __constant_cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE) -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int -+net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) -+{ -+ struct net2280 *dev; -+ struct net2280_ep *ep; -+ u32 max, tmp; -+ unsigned long flags; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || !desc || ep->desc || _ep->name == ep0name -+ || desc->bDescriptorType != USB_DT_ENDPOINT) -+ return -EINVAL; -+ dev = ep->dev; -+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return -ESHUTDOWN; -+ -+ /* erratum 0119 workaround ties up an endpoint number */ -+ if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) -+ return -EDOM; -+ -+ /* sanity check ep-e/ep-f since their fifos are small */ -+ max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff; -+ if (ep->num > 4 && max > 64) -+ return -ERANGE; -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ _ep->maxpacket = max & 0x7ff; -+ ep->desc = desc; -+ -+ /* ep_reset() has already been called */ -+ ep->stopped = 0; -+ ep->out_overflow = 0; -+ -+ /* set speed-dependent max packet; may kick in high bandwidth */ -+ set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max); -+ -+ /* FIFO lines can't go to different packets. PIO is ok, so -+ * use it instead of troublesome (non-bulk) multi-packet DMA. -+ */ -+ if (ep->dma && (max % 4) != 0 && use_dma_chaining) { -+ DEBUG (ep->dev, "%s, no dma for maxpacket %d\n", -+ ep->ep.name, ep->ep.maxpacket); -+ ep->dma = NULL; -+ } -+ -+ /* set type, direction, address; reset fifo counters */ -+ writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); -+ tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); -+ if (tmp == USB_ENDPOINT_XFER_INT) { -+ /* erratum 0105 workaround prevents hs NYET */ -+ if (dev->chiprev == 0100 -+ && dev->gadget.speed == USB_SPEED_HIGH -+ && !(desc->bEndpointAddress & USB_DIR_IN)) -+ writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE), -+ &ep->regs->ep_rsp); -+ } else if (tmp == USB_ENDPOINT_XFER_BULK) { -+ /* catch some particularly blatant driver bugs */ -+ if ((dev->gadget.speed == USB_SPEED_HIGH -+ && max != 512) -+ || (dev->gadget.speed == USB_SPEED_FULL -+ && max > 64)) { -+ spin_unlock_irqrestore (&dev->lock, flags); -+ return -ERANGE; -+ } -+ } -+ ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; -+ tmp <<= ENDPOINT_TYPE; -+ tmp |= desc->bEndpointAddress; -+ tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */ -+ tmp |= 1 << ENDPOINT_ENABLE; -+ wmb (); -+ -+ /* for OUT transfers, block the rx fifo until a read is posted */ -+ ep->is_in = (tmp & USB_DIR_IN) != 0; -+ if (!ep->is_in) -+ writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); -+ -+ writel (tmp, &ep->regs->ep_cfg); -+ -+ /* enable irqs */ -+ if (!ep->dma) { /* pio, per-packet */ -+ tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); -+ writel (tmp, &dev->regs->pciirqenb0); -+ -+ tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) -+ | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) -+ | readl (&ep->regs->ep_irqenb); -+ writel (tmp, &ep->regs->ep_irqenb); -+ } else { /* dma, per-request */ -+ tmp = (1 << (8 + ep->num)); /* completion */ -+ tmp |= readl (&dev->regs->pciirqenb1); -+ writel (tmp, &dev->regs->pciirqenb1); -+ -+ /* for short OUT transfers, dma completions can't -+ * advance the queue; do it pio-style, by hand. -+ * NOTE erratum 0112 workaround #2 -+ */ -+ if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { -+ tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); -+ writel (tmp, &ep->regs->ep_irqenb); -+ -+ tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); -+ writel (tmp, &dev->regs->pciirqenb0); -+ } -+ } -+ -+ tmp = desc->bEndpointAddress; -+ DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n", -+ _ep->name, tmp & 0x0f, DIR_STRING (tmp), -+ type_string (desc->bmAttributes), -+ ep->dma ? "dma" : "pio", max); -+ -+ /* pci writes may still be posted */ -+ spin_unlock_irqrestore (&dev->lock, flags); -+ return 0; -+} -+ -+static int handshake (u32 *ptr, u32 mask, u32 done, int usec) -+{ -+ u32 result; -+ -+ do { -+ result = readl (ptr); -+ if (result == ~(u32)0) /* "device unplugged" */ -+ return -ENODEV; -+ result &= mask; -+ if (result == done) -+ return 0; -+ udelay (1); -+ usec--; -+ } while (usec > 0); -+ return -ETIMEDOUT; -+} -+ -+static struct usb_ep_ops net2280_ep_ops; -+ -+static void ep_reset (struct net2280_regs *regs, struct net2280_ep *ep) -+{ -+ u32 tmp; -+ -+ ep->desc = NULL; -+ INIT_LIST_HEAD (&ep->queue); -+ -+ ep->ep.maxpacket = ~0; -+ ep->ep.ops = &net2280_ep_ops; -+ -+ /* disable the dma, irqs, endpoint... */ -+ if (ep->dma) { -+ writel (0, &ep->dma->dmactl); -+ writel ( (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) -+ | (1 << DMA_TRANSACTION_DONE_INTERRUPT) -+ | (1 << DMA_ABORT) -+ , &ep->dma->dmastat); -+ -+ tmp = readl (®s->pciirqenb0); -+ tmp &= ~(1 << ep->num); -+ writel (tmp, ®s->pciirqenb0); -+ } else { -+ tmp = readl (®s->pciirqenb1); -+ tmp &= ~(1 << (8 + ep->num)); /* completion */ -+ writel (tmp, ®s->pciirqenb1); -+ } -+ writel (0, &ep->regs->ep_irqenb); -+ -+ /* init to our chosen defaults, notably so that we NAK OUT -+ * packets until the driver queues a read (+note erratum 0112) -+ */ -+ writel ( (1 << SET_NAK_OUT_PACKETS_MODE) -+ | (1 << SET_NAK_OUT_PACKETS) -+ | (1 << CLEAR_EP_HIDE_STATUS_PHASE) -+ | (1 << CLEAR_INTERRUPT_MODE) -+ | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) -+ | (1 << CLEAR_ENDPOINT_TOGGLE) -+ | (1 << CLEAR_ENDPOINT_HALT) -+ , &ep->regs->ep_rsp); -+ -+ /* scrub most status bits, and flush any fifo state */ -+ writel ( (1 << TIMEOUT) -+ | (1 << USB_STALL_SENT) -+ | (1 << USB_IN_NAK_SENT) -+ | (1 << USB_IN_ACK_RCVD) -+ | (1 << USB_OUT_PING_NAK_SENT) -+ | (1 << USB_OUT_ACK_SENT) -+ | (1 << FIFO_OVERFLOW) -+ | (1 << FIFO_UNDERFLOW) -+ | (1 << FIFO_FLUSH) -+ | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) -+ | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) -+ | (1 << DATA_PACKET_RECEIVED_INTERRUPT) -+ | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) -+ | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) -+ | (1 << DATA_IN_TOKEN_INTERRUPT) -+ , &ep->regs->ep_stat); -+ -+ /* fifo size is handled separately */ -+} -+ -+static void nuke (struct net2280_ep *); -+ -+static int net2280_disable (struct usb_ep *_ep) -+{ -+ struct net2280_ep *ep; -+ unsigned long flags; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || !ep->desc || _ep->name == ep0name) -+ return -EINVAL; -+ -+ spin_lock_irqsave (&ep->dev->lock, flags); -+ nuke (ep); -+ ep_reset (ep->dev->regs, ep); -+ -+ VDEBUG (ep->dev, "disabled %s %s\n", -+ ep->dma ? "dma" : "pio", _ep->name); -+ -+ /* synch memory views with the device */ -+ (void) readl (&ep->regs->ep_cfg); -+ -+ if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) -+ ep->dma = &ep->dev->dma [ep->num - 1]; -+ -+ spin_unlock_irqrestore (&ep->dev->lock, flags); -+ return 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_request * -+net2280_alloc_request (struct usb_ep *_ep, int gfp_flags) -+{ -+ struct net2280_ep *ep; -+ struct net2280_request *req; -+ -+ if (!_ep) -+ return NULL; -+ ep = container_of (_ep, struct net2280_ep, ep); -+ -+ req = kmalloc (sizeof *req, gfp_flags); -+ if (!req) -+ return NULL; -+ -+ memset (req, 0, sizeof *req); -+ req->req.dma = DMA_ADDR_INVALID; -+ INIT_LIST_HEAD (&req->queue); -+ -+ /* this dma descriptor may be swapped with the previous dummy */ -+ if (ep->dma) { -+ struct net2280_dma *td; -+ -+ td = pci_pool_alloc (ep->dev->requests, gfp_flags, -+ &req->td_dma); -+ if (!td) { -+ kfree (req); -+ return NULL; -+ } -+ td->dmacount = 0; /* not VALID */ -+ td->dmaaddr = __constant_cpu_to_le32 (DMA_ADDR_INVALID); -+ td->dmadesc = td->dmaaddr; -+ req->td = td; -+ } -+ return &req->req; -+} -+ -+static void -+net2280_free_request (struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct net2280_ep *ep; -+ struct net2280_request *req; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || !_req) -+ return; -+ -+ req = container_of (_req, struct net2280_request, req); -+ WARN_ON (!list_empty (&req->queue)); -+ if (req->td) -+ pci_pool_free (ep->dev->requests, req->td, req->td_dma); -+ kfree (req); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+#undef USE_KMALLOC -+ -+/* many common platforms have dma-coherent caches, which means that it's -+ * safe to use kmalloc() memory for all i/o buffers without using any -+ * cache flushing calls. (unless you're trying to share cache lines -+ * between dma and non-dma activities, which is a slow idea in any case.) -+ * -+ * other platforms need more care, with 2.5 having a moderately general -+ * solution (which falls down for allocations smaller than one page) -+ * that improves significantly on the 2.4 PCI allocators by removing -+ * the restriction that memory never be freed in_interrupt(). -+ */ -+#if defined(CONFIG_X86) -+#define USE_KMALLOC -+ -+#elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) -+#define USE_KMALLOC -+ -+#elif defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO) -+#define USE_KMALLOC -+ -+/* FIXME there are other cases, including an x86-64 one ... */ -+#endif -+ -+/* allocating buffers this way eliminates dma mapping overhead, which -+ * on some platforms will mean eliminating a per-io buffer copy. with -+ * some kinds of system caches, further tweaks may still be needed. -+ */ -+static void * -+net2280_alloc_buffer ( -+ struct usb_ep *_ep, -+ unsigned bytes, -+ dma_addr_t *dma, -+ int gfp_flags -+) -+{ -+ void *retval; -+ struct net2280_ep *ep; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep) -+ return NULL; -+ *dma = DMA_ADDR_INVALID; -+ -+#if defined(USE_KMALLOC) -+ retval = kmalloc(bytes, gfp_flags); -+ if (retval) -+ *dma = virt_to_phys(retval); -+#else -+ if (ep->dma) { -+ /* one problem with this call is that it wastes memory on -+ * typical 1/N page allocations: it allocates 1..N pages. -+ * another is that it always uses GFP_ATOMIC. -+ */ -+#warning Using pci_alloc_consistent even with buffers smaller than a page. -+ retval = pci_alloc_consistent(ep->dev->pdev, bytes, dma); -+ } else -+ retval = kmalloc(bytes, gfp_flags); -+#endif -+ return retval; -+} -+ -+static void -+net2280_free_buffer ( -+ struct usb_ep *_ep, -+ void *buf, -+ dma_addr_t dma, -+ unsigned bytes -+) { -+ /* free memory into the right allocator */ -+#ifndef USE_KMALLOC -+ if (dma != DMA_ADDR_INVALID) { -+ struct net2280_ep *ep; -+ -+ ep = container_of(_ep, struct net2280_ep, ep); -+ if (!_ep) -+ return; -+ /* one problem with this call is that some platforms -+ * don't allow it to be used in_irq(). -+ */ -+ pci_free_consistent(ep->dev->pdev, bytes, buf, dma); -+ } else -+#endif -+ kfree (buf); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* load a packet into the fifo we use for usb IN transfers. -+ * works for all endpoints. -+ * -+ * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo -+ * at a time, but this code is simpler because it knows it only writes -+ * one packet. ep-a..ep-d should use dma instead. -+ */ -+static void -+write_fifo (struct net2280_ep *ep, struct usb_request *req) -+{ -+ struct net2280_ep_regs *regs = ep->regs; -+ u8 *buf; -+ u32 tmp; -+ unsigned count, total; -+ -+ /* INVARIANT: fifo is currently empty. (testable) */ -+ -+ if (req) { -+ buf = req->buf + req->actual; -+ prefetch (buf); -+ total = req->length - req->actual; -+ } else { -+ total = 0; -+ buf = NULL; -+ } -+ -+ /* write just one packet at a time */ -+ count = ep->ep.maxpacket; -+ if (count > total) /* min() cannot be used on a bitfield */ -+ count = total; -+ -+ VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n", -+ ep->ep.name, count, -+ (count != ep->ep.maxpacket) ? " (short)" : "", -+ req); -+ while (count >= 4) { -+ /* NOTE be careful if you try to align these. fifo lines -+ * should normally be full (4 bytes) and successive partial -+ * lines are ok only in certain cases. -+ */ -+ tmp = get_unaligned ((u32 *)buf); -+ cpu_to_le32s (&tmp); -+ writel (tmp, ®s->ep_data); -+ buf += 4; -+ count -= 4; -+ } -+ -+ /* last fifo entry is "short" unless we wrote a full packet. -+ * also explicitly validate last word in (periodic) transfers -+ * when maxpacket is not a multiple of 4 bytes. -+ */ -+ if (count || total < ep->ep.maxpacket) { -+ tmp = count ? get_unaligned ((u32 *)buf) : count; -+ cpu_to_le32s (&tmp); -+ set_fifo_bytecount (ep, count & 0x03); -+ writel (tmp, ®s->ep_data); -+ } -+ -+ /* pci writes may still be posted */ -+} -+ -+/* work around erratum 0106: PCI and USB race over the OUT fifo. -+ * caller guarantees chiprev 0100, out endpoint is NAKing, and -+ * there's no real data in the fifo. -+ * -+ * NOTE: also used in cases where that erratum doesn't apply: -+ * where the host wrote "too much" data to us. -+ */ -+static void out_flush (struct net2280_ep *ep) -+{ -+ u32 *statp, tmp; -+ -+ ASSERT_OUT_NAKING (ep); -+ -+ statp = &ep->regs->ep_stat; -+ writel ( (1 << DATA_OUT_PING_TOKEN_INTERRUPT) -+ | (1 << DATA_PACKET_RECEIVED_INTERRUPT) -+ , statp); -+ writel ((1 << FIFO_FLUSH), statp); -+ mb (); -+ tmp = readl (statp); -+ if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT) -+ /* high speed did bulk NYET; fifo isn't filling */ -+ && ep->dev->gadget.speed == USB_SPEED_FULL) { -+ unsigned usec; -+ -+ usec = 50; /* 64 byte bulk/interrupt */ -+ handshake (statp, (1 << USB_OUT_PING_NAK_SENT), -+ (1 << USB_OUT_PING_NAK_SENT), usec); -+ /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */ -+ } -+} -+ -+/* unload packet(s) from the fifo we use for usb OUT transfers. -+ * returns true iff the request completed, because of short packet -+ * or the request buffer having filled with full packets. -+ * -+ * for ep-a..ep-d this will read multiple packets out when they -+ * have been accepted. -+ */ -+static int -+read_fifo (struct net2280_ep *ep, struct net2280_request *req) -+{ -+ struct net2280_ep_regs *regs = ep->regs; -+ u8 *buf = req->req.buf + req->req.actual; -+ unsigned count, tmp, is_short; -+ unsigned cleanup = 0, prevent = 0; -+ -+ /* erratum 0106 ... packets coming in during fifo reads might -+ * be incompletely rejected. not all cases have workarounds. -+ */ -+ if (ep->dev->chiprev == 0x0100 -+ && ep->dev->gadget.speed == USB_SPEED_FULL) { -+ udelay (1); -+ tmp = readl (&ep->regs->ep_stat); -+ if ((tmp & (1 << NAK_OUT_PACKETS))) -+ cleanup = 1; -+ else if ((tmp & (1 << FIFO_FULL))) { -+ start_out_naking (ep); -+ prevent = 1; -+ } -+ /* else: hope we don't see the problem */ -+ } -+ -+ /* never overflow the rx buffer. the fifo reads packets until -+ * it sees a short one; we might not be ready for them all. -+ */ -+ prefetchw (buf); -+ count = readl (®s->ep_avail); -+ if (unlikely (count == 0)) { -+ udelay (1); -+ tmp = readl (&ep->regs->ep_stat); -+ count = readl (®s->ep_avail); -+ /* handled that data already? */ -+ if (count == 0 && (tmp & (1 << NAK_OUT_PACKETS)) == 0) -+ return 0; -+ } -+ -+ tmp = req->req.length - req->req.actual; -+ if (count > tmp) { -+ /* as with DMA, data overflow gets flushed */ -+ if ((tmp % ep->ep.maxpacket) != 0) { -+ ERROR (ep->dev, -+ "%s out fifo %d bytes, expected %d\n", -+ ep->ep.name, count, tmp); -+ req->req.status = -EOVERFLOW; -+ cleanup = 1; -+ /* NAK_OUT_PACKETS will be set, so flushing is safe; -+ * the next read will start with the next packet -+ */ -+ } /* else it's a ZLP, no worries */ -+ count = tmp; -+ } -+ req->req.actual += count; -+ -+ is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0); -+ -+ VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n", -+ ep->ep.name, count, is_short ? " (short)" : "", -+ cleanup ? " flush" : "", prevent ? " nak" : "", -+ req, req->req.actual, req->req.length); -+ -+ while (count >= 4) { -+ tmp = readl (®s->ep_data); -+ cpu_to_le32s (&tmp); -+ put_unaligned (tmp, (u32 *)buf); -+ buf += 4; -+ count -= 4; -+ } -+ if (count) { -+ tmp = readl (®s->ep_data); -+ /* LE conversion is implicit here: */ -+ do { -+ *buf++ = (u8) tmp; -+ tmp >>= 8; -+ } while (--count); -+ } -+ if (cleanup) -+ out_flush (ep); -+ if (prevent) { -+ writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); -+ (void) readl (&ep->regs->ep_rsp); -+ } -+ -+ return is_short || ((req->req.actual == req->req.length) -+ && !req->req.zero); -+} -+ -+/* fill out dma descriptor to match a given request */ -+static void -+fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) -+{ -+ struct net2280_dma *td = req->td; -+ u32 dmacount = req->req.length; -+ -+ /* don't let DMA continue after a short OUT packet, -+ * so overruns can't affect the next transfer. -+ * in case of overruns on max-size packets, we can't -+ * stop the fifo from filling but we can flush it. -+ */ -+ if (ep->is_in) -+ dmacount |= (1 << DMA_DIRECTION); -+ else if ((dmacount % ep->ep.maxpacket) != 0) -+ dmacount |= (1 << END_OF_CHAIN); -+ -+ req->valid = valid; -+ if (valid) -+ dmacount |= (1 << VALID_BIT); -+ if (likely(!req->req.no_interrupt || !use_dma_chaining)) -+ dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE); -+ -+ /* td->dmadesc = previously set by caller */ -+ td->dmaaddr = cpu_to_le32p (&req->req.dma); -+ -+ /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */ -+ wmb (); -+ td->dmacount = cpu_to_le32p (&dmacount); -+} -+ -+static const u32 dmactl_default = -+ (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) -+ | (1 << DMA_CLEAR_COUNT_ENABLE) -+ /* erratum 0116 workaround part 1 (use POLLING) */ -+ | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) -+ | (1 << DMA_VALID_BIT_POLLING_ENABLE) -+ | (1 << DMA_VALID_BIT_ENABLE) -+ | (1 << DMA_SCATTER_GATHER_ENABLE) -+ /* erratum 0116 workaround part 2 (no AUTOSTART) */ -+ | (1 << DMA_ENABLE); -+ -+static inline void spin_stop_dma (struct net2280_dma_regs *dma) -+{ -+ handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50); -+} -+ -+static inline void stop_dma (struct net2280_dma_regs *dma) -+{ -+ writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl); -+ spin_stop_dma (dma); -+} -+ -+static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma) -+{ -+ struct net2280_dma_regs *dma = ep->dma; -+ -+ writel ((1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION), -+ &dma->dmacount); -+ writel (readl (&dma->dmastat), &dma->dmastat); -+ -+ writel (td_dma, &dma->dmadesc); -+ writel (dmactl, &dma->dmactl); -+ -+ /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ -+ (void) readl (&ep->dev->pci->pcimstctl); -+ -+ writel ((1 << DMA_START), &dma->dmastat); -+ -+ if (!ep->is_in) -+ stop_out_naking (ep); -+} -+ -+static void start_dma (struct net2280_ep *ep, struct net2280_request *req) -+{ -+ u32 tmp; -+ struct net2280_dma_regs *dma = ep->dma; -+ -+ /* FIXME can't use DMA for ZLPs */ -+ -+ /* on this path we "know" there's no dma active (yet) */ -+ WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE)); -+ writel (0, &ep->dma->dmactl); -+ -+ /* previous OUT packet might have been short */ -+ if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat)) -+ & (1 << NAK_OUT_PACKETS)) != 0) { -+ writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT), -+ &ep->regs->ep_stat); -+ -+ tmp = readl (&ep->regs->ep_avail); -+ if (tmp) { -+ writel (readl (&dma->dmastat), &dma->dmastat); -+ -+ /* transfer all/some fifo data */ -+ writel (req->req.dma, &dma->dmaaddr); -+ tmp = min (tmp, req->req.length); -+ -+ /* dma irq, faking scatterlist status */ -+ req->td->dmacount = cpu_to_le32 (req->req.length - tmp); -+ writel ((1 << DMA_DONE_INTERRUPT_ENABLE) -+ | tmp, &dma->dmacount); -+ req->td->dmadesc = 0; -+ req->valid = 1; -+ -+ writel ((1 << DMA_ENABLE), &dma->dmactl); -+ writel ((1 << DMA_START), &dma->dmastat); -+ return; -+ } -+ } -+ -+ tmp = dmactl_default; -+ -+ /* force packet boundaries between dma requests, but prevent the -+ * controller from automagically writing a last "short" packet -+ * (zero length) unless the driver explicitly said to do that. -+ */ -+ if (ep->is_in) { -+ if (likely ((req->req.length % ep->ep.maxpacket) != 0 -+ || req->req.zero)) { -+ tmp |= (1 << DMA_FIFO_VALIDATE); -+ ep->in_fifo_validate = 1; -+ } else -+ ep->in_fifo_validate = 0; -+ } -+ -+ /* init req->td, pointing to the current dummy */ -+ req->td->dmadesc = cpu_to_le32 (ep->td_dma); -+ fill_dma_desc (ep, req, 1); -+ -+ if (!use_dma_chaining) -+ req->td->dmacount |= __constant_cpu_to_le32 (1 << END_OF_CHAIN); -+ -+ start_queue (ep, tmp, req->td_dma); -+} -+ -+static inline void -+queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid) -+{ -+ struct net2280_dma *end; -+ dma_addr_t tmp; -+ -+ /* swap new dummy for old, link; fill and maybe activate */ -+ end = ep->dummy; -+ ep->dummy = req->td; -+ req->td = end; -+ -+ tmp = ep->td_dma; -+ ep->td_dma = req->td_dma; -+ req->td_dma = tmp; -+ -+ end->dmadesc = cpu_to_le32 (ep->td_dma); -+ -+ fill_dma_desc (ep, req, valid); -+} -+ -+static void -+done (struct net2280_ep *ep, struct net2280_request *req, int status) -+{ -+ struct net2280 *dev; -+ unsigned stopped = ep->stopped; -+ -+ list_del_init (&req->queue); -+ -+ if (req->req.status == -EINPROGRESS) -+ req->req.status = status; -+ else -+ status = req->req.status; -+ -+ dev = ep->dev; -+ if (req->mapped) { -+ pci_unmap_single (dev->pdev, req->req.dma, req->req.length, -+ ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); -+ req->req.dma = DMA_ADDR_INVALID; -+ req->mapped = 0; -+ } -+ -+ if (status && status != -ESHUTDOWN) -+ VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n", -+ ep->ep.name, &req->req, status, -+ req->req.actual, req->req.length); -+ -+ /* don't modify queue heads during completion callback */ -+ ep->stopped = 1; -+ spin_unlock (&dev->lock); -+ req->req.complete (&ep->ep, &req->req); -+ spin_lock (&dev->lock); -+ ep->stopped = stopped; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int -+net2280_queue (struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) -+{ -+ struct net2280_request *req; -+ struct net2280_ep *ep; -+ struct net2280 *dev; -+ unsigned long flags; -+ -+ /* we always require a cpu-view buffer, so that we can -+ * always use pio (as fallback or whatever). -+ */ -+ req = container_of (_req, struct net2280_request, req); -+ if (!_req || !_req->complete || !_req->buf -+ || !list_empty (&req->queue)) -+ return -EINVAL; -+ if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) -+ return -EDOM; -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || (!ep->desc && ep->num != 0)) -+ return -EINVAL; -+ dev = ep->dev; -+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return -ESHUTDOWN; -+ -+ /* FIXME implement PIO fallback for ZLPs with DMA */ -+ if (ep->dma && _req->length == 0) -+ return -EOPNOTSUPP; -+ -+ /* set up dma mapping in case the caller didn't */ -+ if (ep->dma && _req->dma == DMA_ADDR_INVALID) { -+ _req->dma = pci_map_single (dev->pdev, _req->buf, _req->length, -+ ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); -+ req->mapped = 1; -+ } -+ -+#if 0 -+ VDEBUG (dev, "%s queue req %p, len %d buf %p\n", -+ _ep->name, _req, _req->length, _req->buf); -+#endif -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ -+ _req->status = -EINPROGRESS; -+ _req->actual = 0; -+ -+ /* kickstart this i/o queue? */ -+ if (list_empty (&ep->queue) && !ep->stopped) { -+ /* use DMA if the endpoint supports it, else pio */ -+ if (ep->dma) -+ start_dma (ep, req); -+ else { -+ /* maybe there's no control data, just status ack */ -+ if (ep->num == 0 && _req->length == 0) { -+ allow_status (ep); -+ done (ep, req, 0); -+ VDEBUG (dev, "%s status ack\n", ep->ep.name); -+ goto done; -+ } -+ -+ /* PIO ... stuff the fifo, or unblock it. */ -+ if (ep->is_in) -+ write_fifo (ep, _req); -+ else if (list_empty (&ep->queue)) { -+ u32 s; -+ -+ /* OUT FIFO might have packet(s) buffered */ -+ s = readl (&ep->regs->ep_stat); -+ if ((s & (1 << FIFO_EMPTY)) == 0) { -+ /* note: _req->short_not_ok is -+ * ignored here since PIO _always_ -+ * stops queue advance here, and -+ * _req->status doesn't change for -+ * short reads (only _req->actual) -+ */ -+ if (read_fifo (ep, req)) { -+ done (ep, req, 0); -+ if (ep->num == 0) -+ allow_status (ep); -+ /* don't queue it */ -+ req = NULL; -+ } else -+ s = readl (&ep->regs->ep_stat); -+ } -+ -+ /* don't NAK, let the fifo fill */ -+ if (req && (s & (1 << NAK_OUT_PACKETS))) -+ writel ((1 << CLEAR_NAK_OUT_PACKETS), -+ &ep->regs->ep_rsp); -+ } -+ } -+ -+ } else if (ep->dma) { -+ int valid = 1; -+ -+ if (ep->is_in) { -+ int expect; -+ -+ /* preventing magic zlps is per-engine state, not -+ * per-transfer; irq logic must recover hiccups. -+ */ -+ expect = likely (req->req.zero -+ || (req->req.length % ep->ep.maxpacket) != 0); -+ if (expect != ep->in_fifo_validate) -+ valid = 0; -+ } -+ queue_dma (ep, req, valid); -+ -+ } /* else the irq handler advances the queue. */ -+ -+ if (req) -+ list_add_tail (&req->queue, &ep->queue); -+done: -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ /* pci writes may still be posted */ -+ return 0; -+} -+ -+static inline void -+dma_done ( -+ struct net2280_ep *ep, -+ struct net2280_request *req, -+ u32 dmacount, -+ int status -+) -+{ -+ req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); -+ done (ep, req, status); -+} -+ -+static void restart_dma (struct net2280_ep *ep); -+ -+static void scan_dma_completions (struct net2280_ep *ep) -+{ -+ /* only look at descriptors that were "naturally" retired, -+ * so fifo and list head state won't matter -+ */ -+ while (!list_empty (&ep->queue)) { -+ struct net2280_request *req; -+ u32 tmp; -+ -+ req = list_entry (ep->queue.next, -+ struct net2280_request, queue); -+ if (!req->valid) -+ break; -+ rmb (); -+ tmp = le32_to_cpup (&req->td->dmacount); -+ if ((tmp & (1 << VALID_BIT)) != 0) -+ break; -+ -+ /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" -+ * cases where DMA must be aborted; this code handles -+ * all non-abort DMA completions. -+ */ -+ if (unlikely (req->td->dmadesc == 0)) { -+ /* paranoia */ -+ tmp = readl (&ep->dma->dmacount); -+ if (tmp & DMA_BYTE_COUNT_MASK) -+ break; -+ /* single transfer mode */ -+ dma_done (ep, req, tmp, 0); -+ break; -+ } else if (!ep->is_in -+ && (req->req.length % ep->ep.maxpacket) != 0) { -+ tmp = readl (&ep->regs->ep_stat); -+ -+ /* AVOID TROUBLE HERE by not issuing short reads from -+ * your gadget driver. That helps avoids errata 0121, -+ * 0122, and 0124; not all cases trigger the warning. -+ */ -+ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { -+ WARN (ep->dev, "%s lost packet sync!\n", -+ ep->ep.name); -+ req->req.status = -EOVERFLOW; -+ } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { -+ /* fifo gets flushed later */ -+ ep->out_overflow = 1; -+ DEBUG (ep->dev, "%s dma, discard %d len %d\n", -+ ep->ep.name, tmp, -+ req->req.length); -+ req->req.status = -EOVERFLOW; -+ } -+ } -+ dma_done (ep, req, tmp, 0); -+ } -+} -+ -+static void restart_dma (struct net2280_ep *ep) -+{ -+ struct net2280_request *req; -+ u32 dmactl = dmactl_default; -+ -+ if (ep->stopped) -+ return; -+ req = list_entry (ep->queue.next, struct net2280_request, queue); -+ -+ if (!use_dma_chaining) { -+ start_dma (ep, req); -+ return; -+ } -+ -+ /* the 2280 will be processing the queue unless queue hiccups after -+ * the previous transfer: -+ * IN: wanted automagic zlp, head doesn't (or vice versa) -+ * DMA_FIFO_VALIDATE doesn't init from dma descriptors. -+ * OUT: was "usb-short", we must restart. -+ */ -+ if (ep->is_in && !req->valid) { -+ struct net2280_request *entry, *prev = NULL; -+ int reqmode, done = 0; -+ -+ DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); -+ ep->in_fifo_validate = likely (req->req.zero -+ || (req->req.length % ep->ep.maxpacket) != 0); -+ if (ep->in_fifo_validate) -+ dmactl |= (1 << DMA_FIFO_VALIDATE); -+ list_for_each_entry (entry, &ep->queue, queue) { -+ u32 dmacount; -+ -+ if (entry == req) -+ continue; -+ dmacount = entry->td->dmacount; -+ if (!done) { -+ reqmode = likely (entry->req.zero -+ || (entry->req.length -+ % ep->ep.maxpacket) != 0); -+ if (reqmode == ep->in_fifo_validate) { -+ entry->valid = 1; -+ dmacount |= valid_bit; -+ entry->td->dmacount = dmacount; -+ prev = entry; -+ continue; -+ } else { -+ /* force a hiccup */ -+ prev->td->dmacount |= dma_done_ie; -+ done = 1; -+ } -+ } -+ -+ /* walk the rest of the queue so unlinks behave */ -+ entry->valid = 0; -+ dmacount &= ~valid_bit; -+ entry->td->dmacount = dmacount; -+ prev = entry; -+ } -+ } -+ -+ writel (0, &ep->dma->dmactl); -+ start_queue (ep, dmactl, req->td_dma); -+} -+ -+static void abort_dma (struct net2280_ep *ep) -+{ -+ /* abort the current transfer */ -+ if (likely (!list_empty (&ep->queue))) { -+ /* FIXME work around errata 0121, 0122, 0124 */ -+ writel ((1 << DMA_ABORT), &ep->dma->dmastat); -+ spin_stop_dma (ep->dma); -+ } else -+ stop_dma (ep->dma); -+ scan_dma_completions (ep); -+} -+ -+/* dequeue ALL requests */ -+static void nuke (struct net2280_ep *ep) -+{ -+ struct net2280_request *req; -+ -+ /* called with spinlock held */ -+ ep->stopped = 1; -+ if (ep->dma) -+ abort_dma (ep); -+ while (!list_empty (&ep->queue)) { -+ req = list_entry (ep->queue.next, -+ struct net2280_request, -+ queue); -+ done (ep, req, -ESHUTDOWN); -+ } -+} -+ -+/* dequeue JUST ONE request */ -+static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct net2280_ep *ep; -+ struct net2280_request *req; -+ unsigned long flags; -+ u32 dmactl; -+ int stopped; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || (!ep->desc && ep->num != 0) || !_req) -+ return -EINVAL; -+ -+ spin_lock_irqsave (&ep->dev->lock, flags); -+ stopped = ep->stopped; -+ -+ /* quiesce dma while we patch the queue */ -+ dmactl = 0; -+ ep->stopped = 1; -+ if (ep->dma) { -+ dmactl = readl (&ep->dma->dmactl); -+ /* WARNING erratum 0127 may kick in ... */ -+ stop_dma (ep->dma); -+ scan_dma_completions (ep); -+ } -+ -+ /* make sure it's still queued on this endpoint */ -+ list_for_each_entry (req, &ep->queue, queue) { -+ if (&req->req == _req) -+ break; -+ } -+ if (&req->req != _req) { -+ spin_unlock_irqrestore (&ep->dev->lock, flags); -+ return -EINVAL; -+ } -+ -+ /* queue head may be partially complete. */ -+ if (ep->queue.next == &req->queue) { -+ if (ep->dma) { -+ DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name); -+ _req->status = -ECONNRESET; -+ abort_dma (ep); -+ if (likely (ep->queue.next == &req->queue)) { -+ // NOTE: misreports single-transfer mode -+ req->td->dmacount = 0; /* invalidate */ -+ dma_done (ep, req, -+ readl (&ep->dma->dmacount), -+ -ECONNRESET); -+ } -+ } else { -+ DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name); -+ done (ep, req, -ECONNRESET); -+ } -+ req = NULL; -+ -+ /* patch up hardware chaining data */ -+ } else if (ep->dma && use_dma_chaining) { -+ if (req->queue.prev == ep->queue.next) { -+ writel (le32_to_cpu (req->td->dmadesc), -+ &ep->dma->dmadesc); -+ if (req->td->dmacount & dma_done_ie) -+ writel (readl (&ep->dma->dmacount) -+ | dma_done_ie, -+ &ep->dma->dmacount); -+ } else { -+ struct net2280_request *prev; -+ -+ prev = list_entry (req->queue.prev, -+ struct net2280_request, queue); -+ prev->td->dmadesc = req->td->dmadesc; -+ if (req->td->dmacount & dma_done_ie) -+ prev->td->dmacount |= dma_done_ie; -+ } -+ } -+ -+ if (req) -+ done (ep, req, -ECONNRESET); -+ ep->stopped = stopped; -+ -+ if (ep->dma) { -+ /* turn off dma on inactive queues */ -+ if (list_empty (&ep->queue)) -+ stop_dma (ep->dma); -+ else if (!ep->stopped) { -+ /* resume current request, or start new one */ -+ if (req) -+ writel (dmactl, &ep->dma->dmactl); -+ else -+ start_dma (ep, list_entry (ep->queue.next, -+ struct net2280_request, queue)); -+ } -+ } -+ -+ spin_unlock_irqrestore (&ep->dev->lock, flags); -+ return req ? 0 : -EOPNOTSUPP; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int net2280_fifo_status (struct usb_ep *_ep); -+ -+static int -+net2280_set_halt (struct usb_ep *_ep, int value) -+{ -+ struct net2280_ep *ep; -+ unsigned long flags; -+ int retval = 0; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || (!ep->desc && ep->num != 0)) -+ return -EINVAL; -+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return -ESHUTDOWN; -+ if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03) -+ == USB_ENDPOINT_XFER_ISOC) -+ return -EINVAL; -+ -+ spin_lock_irqsave (&ep->dev->lock, flags); -+ if (!list_empty (&ep->queue)) -+ retval = -EAGAIN; -+ else if (ep->is_in && value && net2280_fifo_status (_ep) != 0) -+ retval = -EAGAIN; -+ else { -+ VDEBUG (ep->dev, "%s %s halt\n", _ep->name, -+ value ? "set" : "clear"); -+ /* set/clear, then synch memory views with the device */ -+ if (value) { -+ if (ep->num == 0) -+ ep->dev->protocol_stall = 1; -+ else -+ set_halt (ep); -+ } else -+ clear_halt (ep); -+ (void) readl (&ep->regs->ep_rsp); -+ } -+ spin_unlock_irqrestore (&ep->dev->lock, flags); -+ -+ return retval; -+} -+ -+static int -+net2280_fifo_status (struct usb_ep *_ep) -+{ -+ struct net2280_ep *ep; -+ u32 avail; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || (!ep->desc && ep->num != 0)) -+ return -ENODEV; -+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return -ESHUTDOWN; -+ -+ avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1); -+ if (avail > ep->fifo_size) -+ return -EOVERFLOW; -+ if (ep->is_in) -+ avail = ep->fifo_size - avail; -+ return avail; -+} -+ -+static void -+net2280_fifo_flush (struct usb_ep *_ep) -+{ -+ struct net2280_ep *ep; -+ -+ ep = container_of (_ep, struct net2280_ep, ep); -+ if (!_ep || (!ep->desc && ep->num != 0)) -+ return; -+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return; -+ -+ writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); -+ (void) readl (&ep->regs->ep_rsp); -+} -+ -+static struct usb_ep_ops net2280_ep_ops = { -+ .enable = net2280_enable, -+ .disable = net2280_disable, -+ -+ .alloc_request = net2280_alloc_request, -+ .free_request = net2280_free_request, -+ -+ .alloc_buffer = net2280_alloc_buffer, -+ .free_buffer = net2280_free_buffer, -+ -+ .queue = net2280_queue, -+ .dequeue = net2280_dequeue, -+ -+ .set_halt = net2280_set_halt, -+ .fifo_status = net2280_fifo_status, -+ .fifo_flush = net2280_fifo_flush, -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int net2280_get_frame (struct usb_gadget *_gadget) -+{ -+ struct net2280 *dev; -+ unsigned long flags; -+ u16 retval; -+ -+ if (!_gadget) -+ return -ENODEV; -+ dev = container_of (_gadget, struct net2280, gadget); -+ spin_lock_irqsave (&dev->lock, flags); -+ retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff; -+ spin_unlock_irqrestore (&dev->lock, flags); -+ return retval; -+} -+ -+static int net2280_wakeup (struct usb_gadget *_gadget) -+{ -+ struct net2280 *dev; -+ u32 tmp; -+ unsigned long flags; -+ -+ if (!_gadget) -+ return 0; -+ dev = container_of (_gadget, struct net2280, gadget); -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ tmp = readl (&dev->usb->usbctl); -+ if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE)) -+ writel (1 << GENERATE_RESUME, &dev->usb->usbstat); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ /* pci writes may still be posted */ -+ return 0; -+} -+ -+static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value) -+{ -+ struct net2280 *dev; -+ u32 tmp; -+ unsigned long flags; -+ -+ if (!_gadget) -+ return 0; -+ dev = container_of (_gadget, struct net2280, gadget); -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ tmp = readl (&dev->usb->usbctl); -+ if (value) -+ tmp |= (1 << SELF_POWERED_STATUS); -+ else -+ tmp &= ~(1 << SELF_POWERED_STATUS); -+ writel (tmp, &dev->usb->usbctl); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ return 0; -+} -+ -+static int net2280_pullup(struct usb_gadget *_gadget, int is_on) -+{ -+ struct net2280 *dev; -+ u32 tmp; -+ unsigned long flags; -+ -+ if (!_gadget) -+ return -ENODEV; -+ dev = container_of (_gadget, struct net2280, gadget); -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ tmp = readl (&dev->usb->usbctl); -+ dev->softconnect = (is_on != 0); -+ if (is_on) -+ tmp |= (1 << USB_DETECT_ENABLE); -+ else -+ tmp &= ~(1 << USB_DETECT_ENABLE); -+ writel (tmp, &dev->usb->usbctl); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ return 0; -+} -+ -+static const struct usb_gadget_ops net2280_ops = { -+ .get_frame = net2280_get_frame, -+ .wakeup = net2280_wakeup, -+ .set_selfpowered = net2280_set_selfpowered, -+ .pullup = net2280_pullup, -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef USE_SYSFS_DEBUG_FILES -+ -+/* "function" sysfs attribute */ -+static ssize_t -+show_function (struct device *_dev, char *buf) -+{ -+ struct net2280 *dev = dev_get_drvdata (_dev); -+ -+ if (!dev->driver -+ || !dev->driver->function -+ || strlen (dev->driver->function) > PAGE_SIZE) -+ return 0; -+ return snprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function); -+} -+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL); -+ -+static ssize_t -+show_registers (struct device *_dev, char *buf) -+{ -+ struct net2280 *dev; -+ char *next; -+ unsigned size, t; -+ unsigned long flags; -+ int i; -+ u32 t1, t2; -+ char *s; -+ -+ dev = dev_get_drvdata (_dev); -+ next = buf; -+ size = PAGE_SIZE; -+ spin_lock_irqsave (&dev->lock, flags); -+ -+ if (dev->driver) -+ s = dev->driver->driver.name; -+ else -+ s = "(none)"; -+ -+ /* Main Control Registers */ -+ t = snprintf (next, size, "%s version " DRIVER_VERSION -+ ", chiprev %04x, dma %s\n\n" -+ "devinit %03x fifoctl %08x gadget '%s'\n" -+ "pci irqenb0 %02x irqenb1 %08x " -+ "irqstat0 %04x irqstat1 %08x\n", -+ driver_name, dev->chiprev, -+ use_dma -+ ? (use_dma_chaining ? "chaining" : "enabled") -+ : "disabled", -+ readl (&dev->regs->devinit), -+ readl (&dev->regs->fifoctl), -+ s, -+ readl (&dev->regs->pciirqenb0), -+ readl (&dev->regs->pciirqenb1), -+ readl (&dev->regs->irqstat0), -+ readl (&dev->regs->irqstat1)); -+ size -= t; -+ next += t; -+ -+ /* USB Control Registers */ -+ t1 = readl (&dev->usb->usbctl); -+ t2 = readl (&dev->usb->usbstat); -+ if (t1 & (1 << VBUS_PIN)) { -+ if (t2 & (1 << HIGH_SPEED)) -+ s = "high speed"; -+ else if (dev->gadget.speed == USB_SPEED_UNKNOWN) -+ s = "powered"; -+ else -+ s = "full speed"; -+ /* full speed bit (6) not working?? */ -+ } else -+ s = "not attached"; -+ t = snprintf (next, size, -+ "stdrsp %08x usbctl %08x usbstat %08x " -+ "addr 0x%02x (%s)\n", -+ readl (&dev->usb->stdrsp), t1, t2, -+ readl (&dev->usb->ouraddr), s); -+ size -= t; -+ next += t; -+ -+ /* PCI Master Control Registers */ -+ -+ /* DMA Control Registers */ -+ -+ /* Configurable EP Control Registers */ -+ for (i = 0; i < 7; i++) { -+ struct net2280_ep *ep; -+ -+ ep = &dev->ep [i]; -+ if (i && !ep->desc) -+ continue; -+ -+ t1 = readl (&ep->regs->ep_cfg); -+ t2 = readl (&ep->regs->ep_rsp) & 0xff; -+ t = snprintf (next, size, -+ "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" -+ "irqenb %02x\n", -+ ep->ep.name, t1, t2, -+ (t2 & (1 << CLEAR_NAK_OUT_PACKETS)) -+ ? "NAK " : "", -+ (t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE)) -+ ? "hide " : "", -+ (t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR)) -+ ? "CRC " : "", -+ (t2 & (1 << CLEAR_INTERRUPT_MODE)) -+ ? "interrupt " : "", -+ (t2 & (1<<CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)) -+ ? "status " : "", -+ (t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE)) -+ ? "NAKmode " : "", -+ (t2 & (1 << CLEAR_ENDPOINT_TOGGLE)) -+ ? "DATA1 " : "DATA0 ", -+ (t2 & (1 << CLEAR_ENDPOINT_HALT)) -+ ? "HALT " : "", -+ readl (&ep->regs->ep_irqenb)); -+ size -= t; -+ next += t; -+ -+ t = snprintf (next, size, -+ "\tstat %08x avail %04x " -+ "(ep%d%s-%s)%s\n", -+ readl (&ep->regs->ep_stat), -+ readl (&ep->regs->ep_avail), -+ t1 & 0x0f, DIR_STRING (t1), -+ type_string (t1 >> 8), -+ ep->stopped ? "*" : ""); -+ size -= t; -+ next += t; -+ -+ if (!ep->dma) -+ continue; -+ -+ t = snprintf (next, size, -+ " dma\tctl %08x stat %08x count %08x\n" -+ "\taddr %08x desc %08x\n", -+ readl (&ep->dma->dmactl), -+ readl (&ep->dma->dmastat), -+ readl (&ep->dma->dmacount), -+ readl (&ep->dma->dmaaddr), -+ readl (&ep->dma->dmadesc)); -+ size -= t; -+ next += t; -+ -+ } -+ -+ /* Indexed Registers */ -+ // none yet -+ -+ /* Statistics */ -+ t = snprintf (next, size, "\nirqs: "); -+ size -= t; -+ next += t; -+ for (i = 0; i < 7; i++) { -+ struct net2280_ep *ep; -+ -+ ep = &dev->ep [i]; -+ if (i && !ep->irqs) -+ continue; -+ t = snprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs); -+ size -= t; -+ next += t; -+ -+ } -+ t = snprintf (next, size, "\n"); -+ size -= t; -+ next += t; -+ -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ return PAGE_SIZE - size; -+} -+static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL); -+ -+static ssize_t -+show_queues (struct device *_dev, char *buf) -+{ -+ struct net2280 *dev; -+ char *next; -+ unsigned size; -+ unsigned long flags; -+ int i; -+ -+ dev = dev_get_drvdata (_dev); -+ next = buf; -+ size = PAGE_SIZE; -+ spin_lock_irqsave (&dev->lock, flags); -+ -+ for (i = 0; i < 7; i++) { -+ struct net2280_ep *ep = &dev->ep [i]; -+ struct net2280_request *req; -+ int t; -+ -+ if (i != 0) { -+ const struct usb_endpoint_descriptor *d; -+ -+ d = ep->desc; -+ if (!d) -+ continue; -+ t = d->bEndpointAddress; -+ t = snprintf (next, size, -+ "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", -+ ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, -+ (t & USB_DIR_IN) ? "in" : "out", -+ ({ char *val; -+ switch (d->bmAttributes & 0x03) { -+ case USB_ENDPOINT_XFER_BULK: -+ val = "bulk"; break; -+ case USB_ENDPOINT_XFER_INT: -+ val = "intr"; break; -+ default: -+ val = "iso"; break; -+ }; val; }), -+ le16_to_cpu (d->wMaxPacketSize) & 0x1fff, -+ ep->dma ? "dma" : "pio", ep->fifo_size -+ ); -+ } else /* ep0 should only have one transfer queued */ -+ t = snprintf (next, size, "ep0 max 64 pio %s\n", -+ ep->is_in ? "in" : "out"); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ -+ if (list_empty (&ep->queue)) { -+ t = snprintf (next, size, "\t(nothing queued)\n"); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ continue; -+ } -+ list_for_each_entry (req, &ep->queue, queue) { -+ if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc)) -+ t = snprintf (next, size, -+ "\treq %p len %d/%d " -+ "buf %p (dmacount %08x)\n", -+ &req->req, req->req.actual, -+ req->req.length, req->req.buf, -+ readl (&ep->dma->dmacount)); -+ else -+ t = snprintf (next, size, -+ "\treq %p len %d/%d buf %p\n", -+ &req->req, req->req.actual, -+ req->req.length, req->req.buf); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ -+ if (ep->dma) { -+ struct net2280_dma *td; -+ -+ td = req->td; -+ t = snprintf (next, size, "\t td %08x " -+ " count %08x buf %08x desc %08x\n", -+ req->td_dma, td->dmacount, -+ td->dmaaddr, td->dmadesc); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ } -+ } -+ } -+ -+done: -+ spin_unlock_irqrestore (&dev->lock, flags); -+ return PAGE_SIZE - size; -+} -+static DEVICE_ATTR (queues, S_IRUGO, show_queues, NULL); -+ -+ -+#else -+ -+#define device_create_file(a,b) do {} while (0) -+#define device_remove_file device_create_file -+ -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* another driver-specific mode might be a request type doing dma -+ * to/from another device fifo instead of to/from memory. -+ */ -+ -+static void set_fifo_mode (struct net2280 *dev, int mode) -+{ -+ /* keeping high bits preserves BAR2 */ -+ writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl); -+ -+ /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */ -+ INIT_LIST_HEAD (&dev->gadget.ep_list); -+ list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list); -+ list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list); -+ switch (mode) { -+ case 0: -+ list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); -+ list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list); -+ dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024; -+ break; -+ case 1: -+ dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048; -+ break; -+ case 2: -+ list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list); -+ dev->ep [1].fifo_size = 2048; -+ dev->ep [2].fifo_size = 1024; -+ break; -+ } -+ /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */ -+ list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list); -+ list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list); -+} -+ -+/** -+ * net2280_set_fifo_mode - change allocation of fifo buffers -+ * @gadget: access to the net2280 device that will be updated -+ * @mode: 0 for default, four 1kB buffers (ep-a through ep-d); -+ * 1 for two 2kB buffers (ep-a and ep-b only); -+ * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c). -+ * -+ * returns zero on success, else negative errno. when this succeeds, -+ * the contents of gadget->ep_list may have changed. -+ * -+ * you may only call this function when endpoints a-d are all disabled. -+ * use it whenever extra hardware buffering can help performance, such -+ * as before enabling "high bandwidth" interrupt endpoints that use -+ * maxpacket bigger than 512 (when double buffering would otherwise -+ * be unavailable). -+ */ -+int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode) -+{ -+ int i; -+ struct net2280 *dev; -+ int status = 0; -+ unsigned long flags; -+ -+ if (!gadget) -+ return -ENODEV; -+ dev = container_of (gadget, struct net2280, gadget); -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ -+ for (i = 1; i <= 4; i++) -+ if (dev->ep [i].desc) { -+ status = -EINVAL; -+ break; -+ } -+ if (mode < 0 || mode > 2) -+ status = -EINVAL; -+ if (status == 0) -+ set_fifo_mode (dev, mode); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ if (status == 0) { -+ if (mode == 1) -+ DEBUG (dev, "fifo: ep-a 2K, ep-b 2K\n"); -+ else if (mode == 2) -+ DEBUG (dev, "fifo: ep-a 2K, ep-b 1K, ep-c 1K\n"); -+ /* else all are 1K */ -+ } -+ return status; -+} -+EXPORT_SYMBOL (net2280_set_fifo_mode); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* keeping it simple: -+ * - one bus driver, initted first; -+ * - one function driver, initted second -+ * -+ * most of the work to support multiple net2280 controllers would -+ * be to associate this gadget driver (yes?) with all of them, or -+ * perhaps to bind specific drivers to specific devices. -+ */ -+ -+static struct net2280 *the_controller; -+ -+static void usb_reset (struct net2280 *dev) -+{ -+ u32 tmp; -+ -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ (void) readl (&dev->usb->usbctl); -+ -+ net2280_led_init (dev); -+ -+ /* disable automatic responses, and irqs */ -+ writel (0, &dev->usb->stdrsp); -+ writel (0, &dev->regs->pciirqenb0); -+ writel (0, &dev->regs->pciirqenb1); -+ -+ /* clear old dma and irq state */ -+ for (tmp = 0; tmp < 4; tmp++) { -+ struct net2280_ep *ep = &dev->ep [tmp + 1]; -+ -+ if (ep->dma) -+ abort_dma (ep); -+ } -+ writel (~0, &dev->regs->irqstat0), -+ writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), -+ -+ /* reset, and enable pci */ -+ tmp = readl (&dev->regs->devinit) -+ | (1 << PCI_ENABLE) -+ | (1 << FIFO_SOFT_RESET) -+ | (1 << USB_SOFT_RESET) -+ | (1 << M8051_RESET); -+ writel (tmp, &dev->regs->devinit); -+ -+ /* standard fifo and endpoint allocations */ -+ set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0); -+} -+ -+static void usb_reinit (struct net2280 *dev) -+{ -+ u32 tmp; -+ int init_dma; -+ -+ /* use_dma changes are ignored till next device re-init */ -+ init_dma = use_dma; -+ -+ /* basic endpoint init */ -+ for (tmp = 0; tmp < 7; tmp++) { -+ struct net2280_ep *ep = &dev->ep [tmp]; -+ -+ ep->ep.name = ep_name [tmp]; -+ ep->dev = dev; -+ ep->num = tmp; -+ -+ if (tmp > 0 && tmp <= 4) { -+ ep->fifo_size = 1024; -+ if (init_dma) -+ ep->dma = &dev->dma [tmp - 1]; -+ } else -+ ep->fifo_size = 64; -+ ep->regs = &dev->epregs [tmp]; -+ ep_reset (dev->regs, ep); -+ } -+ dev->ep [0].ep.maxpacket = 64; -+ dev->ep [5].ep.maxpacket = 64; -+ dev->ep [6].ep.maxpacket = 64; -+ -+ dev->gadget.ep0 = &dev->ep [0].ep; -+ dev->ep [0].stopped = 0; -+ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); -+ -+ /* we want to prevent lowlevel/insecure access from the USB host, -+ * but erratum 0119 means this enable bit is ignored -+ */ -+ for (tmp = 0; tmp < 5; tmp++) -+ writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg); -+} -+ -+static void ep0_start (struct net2280 *dev) -+{ -+ writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE) -+ | (1 << CLEAR_NAK_OUT_PACKETS) -+ | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) -+ , &dev->epregs [0].ep_rsp); -+ -+ /* -+ * hardware optionally handles a bunch of standard requests -+ * that the API hides from drivers anyway. have it do so. -+ * endpoint status/features are handled in software, to -+ * help pass tests for some dubious behavior. -+ */ -+ writel ( (1 << SET_TEST_MODE) -+ | (1 << SET_ADDRESS) -+ | (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) -+ | (1 << GET_DEVICE_STATUS) -+ | (1 << GET_INTERFACE_STATUS) -+ , &dev->usb->stdrsp); -+ writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE) -+ | (1 << SELF_POWERED_USB_DEVICE) -+ /* erratum 0102 workaround */ -+ | ((dev->chiprev == 0100) ? 0 : 1) << SUSPEND_IMMEDIATELY -+ | (1 << REMOTE_WAKEUP_SUPPORT) -+ | (dev->softconnect << USB_DETECT_ENABLE) -+ | (1 << SELF_POWERED_STATUS) -+ , &dev->usb->usbctl); -+ -+ /* enable irqs so we can see ep0 and general operation */ -+ writel ( (1 << SETUP_PACKET_INTERRUPT_ENABLE) -+ | (1 << ENDPOINT_0_INTERRUPT_ENABLE) -+ , &dev->regs->pciirqenb0); -+ writel ( (1 << PCI_INTERRUPT_ENABLE) -+ | (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) -+ | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) -+ | (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE) -+ | (1 << VBUS_INTERRUPT_ENABLE) -+ | (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE) -+ | (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) -+ , &dev->regs->pciirqenb1); -+ -+ /* don't leave any writes posted */ -+ (void) readl (&dev->usb->usbctl); -+} -+ -+/* when a driver is successfully registered, it will receive -+ * control requests including set_configuration(), which enables -+ * non-control requests. then usb traffic follows until a -+ * disconnect is reported. then a host may connect again, or -+ * the driver might get unbound. -+ */ -+int usb_gadget_register_driver (struct usb_gadget_driver *driver) -+{ -+ struct net2280 *dev = the_controller; -+ int retval; -+ unsigned i; -+ -+ /* insist on high speed support from the driver, since -+ * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE) -+ * "must not be used in normal operation" -+ */ -+ if (!driver -+ || driver->speed != USB_SPEED_HIGH -+ || !driver->bind -+ || !driver->unbind -+ || !driver->setup) -+ return -EINVAL; -+ if (!dev) -+ return -ENODEV; -+ if (dev->driver) -+ return -EBUSY; -+ -+ for (i = 0; i < 7; i++) -+ dev->ep [i].irqs = 0; -+ -+ /* hook up the driver ... */ -+ dev->softconnect = 1; -+ dev->driver = driver; -+ retval = driver->bind (&dev->gadget); -+ if (retval) { -+ DEBUG (dev, "bind to driver %s --> %d\n", -+ driver->driver.name, retval); -+ dev->driver = 0; -+ return retval; -+ } -+ -+ /* ... then enable host detection and ep0; and we're ready -+ * for set_configuration as well as eventual disconnect. -+ */ -+ net2280_led_active (dev, 1); -+ ep0_start (dev); -+ -+ DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n", -+ driver->driver.name, -+ readl (&dev->usb->usbctl), -+ readl (&dev->usb->stdrsp)); -+ -+ /* pci writes may still be posted */ -+ return 0; -+} -+EXPORT_SYMBOL (usb_gadget_register_driver); -+ -+static void -+stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver) -+{ -+ int i; -+ -+ /* don't disconnect if it's not connected */ -+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) -+ driver = NULL; -+ -+ /* stop hardware; prevent new request submissions; -+ * and kill any outstanding requests. -+ */ -+ usb_reset (dev); -+ for (i = 0; i < 7; i++) -+ nuke (&dev->ep [i]); -+ -+ /* report disconnect; the driver is already quiesced */ -+ if (driver) { -+ spin_unlock (&dev->lock); -+ driver->disconnect (&dev->gadget); -+ spin_lock (&dev->lock); -+ } -+ -+ usb_reinit (dev); -+} -+ -+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) -+{ -+ struct net2280 *dev = the_controller; -+ unsigned long flags; -+ -+ if (!dev) -+ return -ENODEV; -+ if (!driver || driver != dev->driver) -+ return -EINVAL; -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ stop_activity (dev, driver); -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ driver->unbind (&dev->gadget); -+ dev->driver = 0; -+ -+ net2280_led_active (dev, 0); -+ -+ DEBUG (dev, "unregistered driver '%s'\n", driver->driver.name); -+ return 0; -+} -+EXPORT_SYMBOL (usb_gadget_unregister_driver); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq. -+ * also works for dma-capable endpoints, in pio mode or just -+ * to manually advance the queue after short OUT transfers. -+ */ -+static void handle_ep_small (struct net2280_ep *ep) -+{ -+ struct net2280_request *req; -+ u32 t; -+ /* 0 error, 1 mid-data, 2 done */ -+ int mode = 1; -+ -+ if (!list_empty (&ep->queue)) -+ req = list_entry (ep->queue.next, -+ struct net2280_request, queue); -+ else -+ req = NULL; -+ -+ /* ack all, and handle what we care about */ -+ t = readl (&ep->regs->ep_stat); -+ ep->irqs++; -+#if 0 -+ VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n", -+ ep->ep.name, t, req ? &req->req : 0); -+#endif -+ writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat); -+ -+ /* for ep0, monitor token irqs to catch data stage length errors -+ * and to synchronize on status. -+ * -+ * also, to defer reporting of protocol stalls ... here's where -+ * data or status first appears, handling stalls here should never -+ * cause trouble on the host side.. -+ * -+ * control requests could be slightly faster without token synch for -+ * status, but status can jam up that way. -+ */ -+ if (unlikely (ep->num == 0)) { -+ if (ep->is_in) { -+ /* status; stop NAKing */ -+ if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) { -+ if (ep->dev->protocol_stall) { -+ ep->stopped = 1; -+ set_halt (ep); -+ } -+ if (!req) -+ allow_status (ep); -+ mode = 2; -+ /* reply to extra IN data tokens with a zlp */ -+ } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { -+ if (ep->dev->protocol_stall) { -+ ep->stopped = 1; -+ set_halt (ep); -+ mode = 2; -+ } else if (!req && ep->stopped) -+ write_fifo (ep, NULL); -+ } -+ } else { -+ /* status; stop NAKing */ -+ if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { -+ if (ep->dev->protocol_stall) { -+ ep->stopped = 1; -+ set_halt (ep); -+ } -+ mode = 2; -+ /* an extra OUT token is an error */ -+ } else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) -+ && req -+ && req->req.actual == req->req.length) -+ || !req) { -+ ep->dev->protocol_stall = 1; -+ set_halt (ep); -+ ep->stopped = 1; -+ if (req) -+ done (ep, req, -EOVERFLOW); -+ req = NULL; -+ } -+ } -+ } -+ -+ if (unlikely (!req)) -+ return; -+ -+ /* manual DMA queue advance after short OUT */ -+ if (likely (ep->dma != 0)) { -+ if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { -+ u32 count; -+ int stopped = ep->stopped; -+ -+ /* TRANSFERRED works around OUT_DONE erratum 0112. -+ * we expect (N <= maxpacket) bytes; host wrote M. -+ * iff (M < N) we won't ever see a DMA interrupt. -+ */ -+ ep->stopped = 1; -+ for (count = 0; ; t = readl (&ep->regs->ep_stat)) { -+ -+ /* any preceding dma transfers must finish. -+ * dma handles (M >= N), may empty the queue -+ */ -+ scan_dma_completions (ep); -+ if (unlikely (list_empty (&ep->queue) -+ || ep->out_overflow)) { -+ req = NULL; -+ break; -+ } -+ req = list_entry (ep->queue.next, -+ struct net2280_request, queue); -+ -+ /* here either (M < N), a "real" short rx; -+ * or (M == N) and the queue didn't empty -+ */ -+ if (likely (t & (1 << FIFO_EMPTY))) { -+ count = readl (&ep->dma->dmacount); -+ count &= DMA_BYTE_COUNT_MASK; -+ if (readl (&ep->dma->dmadesc) -+ != req->td_dma) -+ req = NULL; -+ break; -+ } -+ udelay(1); -+ } -+ -+ /* stop DMA, leave ep NAKing */ -+ writel ((1 << DMA_ABORT), &ep->dma->dmastat); -+ spin_stop_dma (ep->dma); -+ -+ if (likely (req != 0)) { -+ req->td->dmacount = 0; -+ t = readl (&ep->regs->ep_avail); -+ dma_done (ep, req, count, t); -+ } -+ -+ /* also flush to prevent erratum 0106 trouble */ -+ if (unlikely (ep->out_overflow -+ || (ep->dev->chiprev == 0x0100 -+ && ep->dev->gadget.speed -+ == USB_SPEED_FULL))) { -+ out_flush (ep); -+ ep->out_overflow = 0; -+ } -+ -+ /* (re)start dma if needed, stop NAKing */ -+ ep->stopped = stopped; -+ if (!list_empty (&ep->queue)) -+ restart_dma (ep); -+ } else -+ DEBUG (ep->dev, "%s dma ep_stat %08x ??\n", -+ ep->ep.name, t); -+ return; -+ -+ /* data packet(s) received (in the fifo, OUT) */ -+ } else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) { -+ if (read_fifo (ep, req) && ep->num != 0) -+ mode = 2; -+ -+ /* data packet(s) transmitted (IN) */ -+ } else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) { -+ unsigned len; -+ -+ len = req->req.length - req->req.actual; -+ if (len > ep->ep.maxpacket) -+ len = ep->ep.maxpacket; -+ req->req.actual += len; -+ -+ /* if we wrote it all, we're usually done */ -+ if (req->req.actual == req->req.length) { -+ if (ep->num == 0) { -+ /* wait for control status */ -+ if (mode != 2) -+ req = NULL; -+ } else if (!req->req.zero || len != ep->ep.maxpacket) -+ mode = 2; -+ } -+ -+ /* there was nothing to do ... */ -+ } else if (mode == 1) -+ return; -+ -+ /* done */ -+ if (mode == 2) { -+ /* stream endpoints often resubmit/unlink in completion */ -+ done (ep, req, 0); -+ -+ /* maybe advance queue to next request */ -+ if (ep->num == 0) { -+ /* NOTE: net2280 could let gadget driver start the -+ * status stage later. since not all controllers let -+ * them control that, the api doesn't (yet) allow it. -+ */ -+ if (!ep->stopped) -+ allow_status (ep); -+ req = NULL; -+ } else { -+ if (!list_empty (&ep->queue) && !ep->stopped) -+ req = list_entry (ep->queue.next, -+ struct net2280_request, queue); -+ else -+ req = NULL; -+ if (req && !ep->is_in) -+ stop_out_naking (ep); -+ } -+ } -+ -+ /* is there a buffer for the next packet? -+ * for best streaming performance, make sure there is one. -+ */ -+ if (req && !ep->stopped) { -+ -+ /* load IN fifo with next packet (may be zlp) */ -+ if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) -+ write_fifo (ep, &req->req); -+ } -+} -+ -+static struct net2280_ep * -+get_ep_by_addr (struct net2280 *dev, u16 wIndex) -+{ -+ struct net2280_ep *ep; -+ -+ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) -+ return &dev->ep [0]; -+ list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) { -+ u8 bEndpointAddress; -+ -+ if (!ep->desc) -+ continue; -+ bEndpointAddress = ep->desc->bEndpointAddress; -+ if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) -+ continue; -+ if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f)) -+ return ep; -+ } -+ return NULL; -+} -+ -+static void handle_stat0_irqs (struct net2280 *dev, u32 stat) -+{ -+ struct net2280_ep *ep; -+ u32 num, scratch; -+ -+ /* most of these don't need individual acks */ -+ stat &= ~(1 << INTA_ASSERTED); -+ if (!stat) -+ return; -+ // DEBUG (dev, "irqstat0 %04x\n", stat); -+ -+ /* starting a control request? */ -+ if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) { -+ union { -+ u32 raw [2]; -+ struct usb_ctrlrequest r; -+ } u; -+ int tmp = 0; -+ struct net2280_request *req; -+ -+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) { -+ if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED)) -+ dev->gadget.speed = USB_SPEED_HIGH; -+ else -+ dev->gadget.speed = USB_SPEED_FULL; -+ net2280_led_speed (dev, dev->gadget.speed); -+ DEBUG (dev, "%s speed\n", -+ (dev->gadget.speed == USB_SPEED_HIGH) -+ ? "high" : "full"); -+ } -+ -+ ep = &dev->ep [0]; -+ ep->irqs++; -+ -+ /* make sure any leftover request state is cleared */ -+ stat &= ~(1 << ENDPOINT_0_INTERRUPT); -+ while (!list_empty (&ep->queue)) { -+ req = list_entry (ep->queue.next, -+ struct net2280_request, queue); -+ done (ep, req, (req->req.actual == req->req.length) -+ ? 0 : -EPROTO); -+ } -+ ep->stopped = 0; -+ dev->protocol_stall = 0; -+ writel ( (1 << TIMEOUT) -+ | (1 << USB_STALL_SENT) -+ | (1 << USB_IN_NAK_SENT) -+ | (1 << USB_IN_ACK_RCVD) -+ | (1 << USB_OUT_PING_NAK_SENT) -+ | (1 << USB_OUT_ACK_SENT) -+ | (1 << FIFO_OVERFLOW) -+ | (1 << FIFO_UNDERFLOW) -+ | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) -+ | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) -+ | (1 << DATA_PACKET_RECEIVED_INTERRUPT) -+ | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) -+ | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) -+ | (1 << DATA_IN_TOKEN_INTERRUPT) -+ , &ep->regs->ep_stat); -+ u.raw [0] = readl (&dev->usb->setup0123); -+ u.raw [1] = readl (&dev->usb->setup4567); -+ -+ cpu_to_le32s (&u.raw [0]); -+ cpu_to_le32s (&u.raw [1]); -+ -+ le16_to_cpus (&u.r.wValue); -+ le16_to_cpus (&u.r.wIndex); -+ le16_to_cpus (&u.r.wLength); -+ -+ /* ack the irq */ -+ writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0); -+ stat ^= (1 << SETUP_PACKET_INTERRUPT); -+ -+ /* watch control traffic at the token level, and force -+ * synchronization before letting the status stage happen. -+ * FIXME ignore tokens we'll NAK, until driver responds. -+ * that'll mean a lot less irqs for some drivers. -+ */ -+ ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; -+ if (ep->is_in) { -+ scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) -+ | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) -+ | (1 << DATA_IN_TOKEN_INTERRUPT); -+ stop_out_naking (ep); -+ } else -+ scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT) -+ | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) -+ | (1 << DATA_IN_TOKEN_INTERRUPT); -+ writel (scratch, &dev->epregs [0].ep_irqenb); -+ -+ /* we made the hardware handle most lowlevel requests; -+ * everything else goes uplevel to the gadget code. -+ */ -+ switch (u.r.bRequest) { -+ case USB_REQ_GET_STATUS: { -+ struct net2280_ep *e; -+ u16 status; -+ -+ /* hw handles device and interface status */ -+ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT)) -+ goto delegate; -+ if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0 -+ || u.r.wLength > 2) -+ goto do_stall; -+ -+ if (readl (&e->regs->ep_rsp) -+ & (1 << SET_ENDPOINT_HALT)) -+ status = __constant_cpu_to_le16 (1); -+ else -+ status = __constant_cpu_to_le16 (0); -+ -+ /* don't bother with a request object! */ -+ writel (0, &dev->epregs [0].ep_irqenb); -+ set_fifo_bytecount (ep, u.r.wLength); -+ writel (status, &dev->epregs [0].ep_data); -+ allow_status (ep); -+ VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status); -+ goto next_endpoints; -+ } -+ break; -+ case USB_REQ_CLEAR_FEATURE: { -+ struct net2280_ep *e; -+ -+ /* hw handles device features */ -+ if (u.r.bRequestType != USB_RECIP_ENDPOINT) -+ goto delegate; -+ if (u.r.wValue != USB_ENDPOINT_HALT -+ || u.r.wLength != 0) -+ goto do_stall; -+ if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) -+ goto do_stall; -+ clear_halt (e); -+ allow_status (ep); -+ VDEBUG (dev, "%s clear halt\n", ep->ep.name); -+ goto next_endpoints; -+ } -+ break; -+ case USB_REQ_SET_FEATURE: { -+ struct net2280_ep *e; -+ -+ /* hw handles device features */ -+ if (u.r.bRequestType != USB_RECIP_ENDPOINT) -+ goto delegate; -+ if (u.r.wValue != USB_ENDPOINT_HALT -+ || u.r.wLength != 0) -+ goto do_stall; -+ if ((e = get_ep_by_addr (dev, u.r.wIndex)) == 0) -+ goto do_stall; -+ set_halt (e); -+ allow_status (ep); -+ VDEBUG (dev, "%s set halt\n", ep->ep.name); -+ goto next_endpoints; -+ } -+ break; -+ default: -+delegate: -+ VDEBUG (dev, "setup %02x.%02x v%04x i%04x " -+ "ep_cfg %08x\n", -+ u.r.bRequestType, u.r.bRequest, -+ u.r.wValue, u.r.wIndex, -+ readl (&ep->regs->ep_cfg)); -+ spin_unlock (&dev->lock); -+ tmp = dev->driver->setup (&dev->gadget, &u.r); -+ spin_lock (&dev->lock); -+ } -+ -+ /* stall ep0 on error */ -+ if (tmp < 0) { -+do_stall: -+ VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n", -+ u.r.bRequestType, u.r.bRequest, tmp); -+ dev->protocol_stall = 1; -+ } -+ -+ /* some in/out token irq should follow; maybe stall then. -+ * driver must queue a request (even zlp) or halt ep0 -+ * before the host times out. -+ */ -+ } -+ -+next_endpoints: -+ /* endpoint data irq ? */ -+ scratch = stat & 0x7f; -+ stat &= ~0x7f; -+ for (num = 0; scratch; num++) { -+ u32 t; -+ -+ /* do this endpoint's FIFO and queue need tending? */ -+ t = 1 << num; -+ if ((scratch & t) == 0) -+ continue; -+ scratch ^= t; -+ -+ ep = &dev->ep [num]; -+ handle_ep_small (ep); -+ } -+ -+ if (stat) -+ DEBUG (dev, "unhandled irqstat0 %08x\n", stat); -+} -+ -+#define DMA_INTERRUPTS ( \ -+ (1 << DMA_D_INTERRUPT) \ -+ | (1 << DMA_C_INTERRUPT) \ -+ | (1 << DMA_B_INTERRUPT) \ -+ | (1 << DMA_A_INTERRUPT)) -+#define PCI_ERROR_INTERRUPTS ( \ -+ (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \ -+ | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \ -+ | (1 << PCI_RETRY_ABORT_INTERRUPT)) -+ -+static void handle_stat1_irqs (struct net2280 *dev, u32 stat) -+{ -+ struct net2280_ep *ep; -+ u32 tmp, num, scratch; -+ -+ /* after disconnect there's nothing else to do! */ -+ tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT); -+ if (stat & tmp) { -+ writel (tmp, &dev->regs->irqstat1); -+ if (((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) != 0 -+ || (readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0 -+ ) && dev->gadget.speed != USB_SPEED_UNKNOWN) { -+ DEBUG (dev, "disconnect %s\n", -+ dev->driver->driver.name); -+ stop_activity (dev, dev->driver); -+ ep0_start (dev); -+ return; -+ } -+ stat &= ~tmp; -+ -+ /* vBUS can bounce ... one of many reasons to ignore the -+ * notion of hotplug events on bus connect/disconnect! -+ */ -+ if (!stat) -+ return; -+ } -+ -+ /* NOTE: chip stays in PCI D0 state for now, but it could -+ * enter D1 to save more power -+ */ -+ tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT); -+ if (stat & tmp) { -+ writel (tmp, &dev->regs->irqstat1); -+ if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) { -+ if (dev->driver->suspend) -+ dev->driver->suspend (&dev->gadget); -+ /* we use SUSPEND_IMMEDIATELY */ -+ stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT); -+ } else { -+ if (dev->driver->resume) -+ dev->driver->resume (&dev->gadget); -+ /* at high speed, note erratum 0133 */ -+ } -+ stat &= ~tmp; -+ } -+ -+ /* clear any other status/irqs */ -+ if (stat) -+ writel (stat, &dev->regs->irqstat1); -+ -+ /* some status we can just ignore */ -+ stat &= ~((1 << CONTROL_STATUS_INTERRUPT) -+ | (1 << SUSPEND_REQUEST_INTERRUPT) -+ | (1 << RESUME_INTERRUPT) -+ | (1 << SOF_INTERRUPT)); -+ if (!stat) -+ return; -+ // DEBUG (dev, "irqstat1 %08x\n", stat); -+ -+ /* DMA status, for ep-{a,b,c,d} */ -+ scratch = stat & DMA_INTERRUPTS; -+ stat &= ~DMA_INTERRUPTS; -+ scratch >>= 9; -+ for (num = 0; scratch; num++) { -+ struct net2280_dma_regs *dma; -+ -+ tmp = 1 << num; -+ if ((tmp & scratch) == 0) -+ continue; -+ scratch ^= tmp; -+ -+ ep = &dev->ep [num + 1]; -+ dma = ep->dma; -+ -+ if (!dma) -+ continue; -+ -+ /* clear ep's dma status */ -+ tmp = readl (&dma->dmastat); -+ writel (tmp, &dma->dmastat); -+ -+ /* chaining should stop on abort, short OUT from fifo, -+ * or (stat0 codepath) short OUT transfer. -+ */ -+ if (!use_dma_chaining) { -+ if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) -+ == 0) { -+ DEBUG (ep->dev, "%s no xact done? %08x\n", -+ ep->ep.name, tmp); -+ continue; -+ } -+ stop_dma (ep->dma); -+ } -+ -+ /* OUT transfers terminate when the data from the -+ * host is in our memory. Process whatever's done. -+ * On this path, we know transfer's last packet wasn't -+ * less than req->length. NAK_OUT_PACKETS may be set, -+ * or the FIFO may already be holding new packets. -+ * -+ * IN transfers can linger in the FIFO for a very -+ * long time ... we ignore that for now, accounting -+ * precisely (like PIO does) needs per-packet irqs -+ */ -+ scan_dma_completions (ep); -+ -+ /* disable dma on inactive queues; else maybe restart */ -+ if (list_empty (&ep->queue)) { -+ if (use_dma_chaining) -+ stop_dma (ep->dma); -+ } else { -+ tmp = readl (&dma->dmactl); -+ if (!use_dma_chaining -+ || (tmp & (1 << DMA_ENABLE)) == 0) -+ restart_dma (ep); -+ else if (ep->is_in && use_dma_chaining) { -+ struct net2280_request *req; -+ u32 dmacount; -+ -+ /* the descriptor at the head of the chain -+ * may still have VALID_BIT clear; that's -+ * used to trigger changing DMA_FIFO_VALIDATE -+ * (affects automagic zlp writes). -+ */ -+ req = list_entry (ep->queue.next, -+ struct net2280_request, queue); -+ dmacount = req->td->dmacount; -+ dmacount &= __constant_cpu_to_le32 ( -+ (1 << VALID_BIT) -+ | DMA_BYTE_COUNT_MASK); -+ if (dmacount && (dmacount & valid_bit) == 0) -+ restart_dma (ep); -+ } -+ } -+ ep->irqs++; -+ } -+ -+ /* NOTE: there are other PCI errors we might usefully notice. -+ * if they appear very often, here's where to try recovering. -+ */ -+ if (stat & PCI_ERROR_INTERRUPTS) { -+ ERROR (dev, "pci dma error; stat %08x\n", stat); -+ stat &= ~PCI_ERROR_INTERRUPTS; -+ /* these are fatal errors, but "maybe" they won't -+ * happen again ... -+ */ -+ stop_activity (dev, dev->driver); -+ ep0_start (dev); -+ stat = 0; -+ } -+ -+ if (stat) -+ DEBUG (dev, "unhandled irqstat1 %08x\n", stat); -+} -+ -+static irqreturn_t net2280_irq (int irq, void *_dev, struct pt_regs * r) -+{ -+ struct net2280 *dev = _dev; -+ -+ spin_lock (&dev->lock); -+ -+ /* handle disconnect, dma, and more */ -+ handle_stat1_irqs (dev, readl (&dev->regs->irqstat1)); -+ -+ /* control requests and PIO */ -+ handle_stat0_irqs (dev, readl (&dev->regs->irqstat0)); -+ -+ spin_unlock (&dev->lock); -+ -+ return IRQ_HANDLED; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* tear down the binding between this driver and the pci device */ -+ -+static void net2280_remove (struct pci_dev *pdev) -+{ -+ struct net2280 *dev = pci_get_drvdata (pdev); -+ -+ /* start with the driver above us */ -+ if (dev->driver) { -+ /* should have been done already by driver model core */ -+ WARN (dev, "pci remove, driver '%s' is still registered\n", -+ dev->driver->driver.name); -+ usb_gadget_unregister_driver (dev->driver); -+ } -+ -+ /* then clean up the resources we allocated during probe() */ -+ net2280_led_shutdown (dev); -+ if (dev->requests) { -+ int i; -+ for (i = 1; i < 5; i++) { -+ if (!dev->ep [i].dummy) -+ continue; -+ pci_pool_free (dev->requests, dev->ep [i].dummy, -+ dev->ep [i].td_dma); -+ } -+ pci_pool_destroy (dev->requests); -+ } -+ if (dev->got_irq) -+ free_irq (pdev->irq, dev); -+ if (dev->regs) -+ iounmap (dev->regs); -+ if (dev->region) -+ release_mem_region (pci_resource_start (pdev, 0), -+ pci_resource_len (pdev, 0)); -+ if (dev->enabled) -+ pci_disable_device (pdev); -+ pci_set_drvdata (pdev, 0); -+ -+ INFO (dev, "unbind from pci %s\n", pdev->slot_name); -+ -+ kfree (dev); -+ the_controller = 0; -+} -+ -+/* wrap this driver around the specified device, but -+ * don't respond over USB until a gadget driver binds to us. -+ */ -+ -+static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id) -+{ -+ struct net2280 *dev; -+ unsigned long resource, len; -+ void *base = NULL; -+ int retval, i; -+ char buf [8], *bufp; -+ -+ /* if you want to support more than one controller in a system, -+ * usb_gadget_driver_{register,unregister}() must change. -+ */ -+ if (the_controller) { -+ WARN (the_controller, "ignoring %s\n", pdev->slot_name); -+ return -EBUSY; -+ } -+ -+ /* alloc, and start init */ -+ dev = kmalloc (sizeof *dev, SLAB_KERNEL); -+ if (dev == NULL){ -+ retval = -ENOMEM; -+ goto done; -+ } -+ -+ memset (dev, 0, sizeof *dev); -+ spin_lock_init (&dev->lock); -+ dev->pdev = pdev; -+ dev->gadget.ops = &net2280_ops; -+ dev->gadget.is_dualspeed = 1; -+ -+ dev->gadget.dev.bus_id = pdev->slot_name; -+ dev->gadget.name = driver_name; -+ -+ /* now all the pci goodies ... */ -+ if (pci_enable_device (pdev) < 0) { -+ retval = -ENODEV; -+ goto done; -+ } -+ dev->enabled = 1; -+ -+ /* BAR 0 holds all the registers -+ * BAR 1 is 8051 memory; unused here (note erratum 0103) -+ * BAR 2 is fifo memory; unused here -+ */ -+ resource = pci_resource_start (pdev, 0); -+ len = pci_resource_len (pdev, 0); -+ if (!request_mem_region (resource, len, driver_name)) { -+ DEBUG (dev, "controller already in use\n"); -+ retval = -EBUSY; -+ goto done; -+ } -+ dev->region = 1; -+ -+ base = ioremap_nocache (resource, len); -+ if (base == NULL) { -+ DEBUG (dev, "can't map memory\n"); -+ retval = -EFAULT; -+ goto done; -+ } -+ dev->regs = (struct net2280_regs *) base; -+ dev->usb = (struct net2280_usb_regs *) (base + 0x0080); -+ dev->pci = (struct net2280_pci_regs *) (base + 0x0100); -+ dev->dma = (struct net2280_dma_regs *) (base + 0x0180); -+ dev->dep = (struct net2280_dep_regs *) (base + 0x0200); -+ dev->epregs = (struct net2280_ep_regs *) (base + 0x0300); -+ -+ /* put into initial config, link up all endpoints */ -+ writel (0, &dev->usb->usbctl); -+ usb_reset (dev); -+ usb_reinit (dev); -+ -+ /* irq setup after old hardware is cleaned up */ -+ if (!pdev->irq) { -+ ERROR (dev, "No IRQ. Check PCI setup!\n"); -+ retval = -ENODEV; -+ goto done; -+ } -+#ifndef __sparc__ -+ snprintf (buf, sizeof buf, "%d", pdev->irq); -+ bufp = buf; -+#else -+ bufp = __irq_itoa(pdev->irq); -+#endif -+ if (request_irq (pdev->irq, net2280_irq, SA_SHIRQ, driver_name, dev) -+ != 0) { -+ ERROR (dev, "request interrupt %s failed\n", bufp); -+ retval = -EBUSY; -+ goto done; -+ } -+ dev->got_irq = 1; -+ -+ /* DMA setup */ -+ dev->requests = pci_pool_create ("requests", pdev, -+ sizeof (struct net2280_dma), -+ 0 /* no alignment requirements */, -+ 0 /* or page-crossing issues */, -+ SLAB_KERNEL /* 2.4 only */ ); -+ if (!dev->requests) { -+ DEBUG (dev, "can't get request pool\n"); -+ retval = -ENOMEM; -+ goto done; -+ } -+ for (i = 1; i < 5; i++) { -+ struct net2280_dma *td; -+ -+ td = pci_pool_alloc (dev->requests, GFP_KERNEL, -+ &dev->ep [i].td_dma); -+ if (!td) { -+ DEBUG (dev, "can't get dummy %d\n", i); -+ retval = -ENOMEM; -+ goto done; -+ } -+ td->dmacount = 0; /* not VALID */ -+ td->dmaaddr = __constant_cpu_to_le32 (DMA_ADDR_INVALID); -+ td->dmadesc = td->dmaaddr; -+ dev->ep [i].dummy = td; -+ } -+ -+ /* enable lower-overhead pci memory bursts during DMA */ -+ writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) -+ // 256 write retries may not be enough... -+ // | (1 << PCI_RETRY_ABORT_ENABLE) -+ | (1 << DMA_READ_MULTIPLE_ENABLE) -+ | (1 << DMA_READ_LINE_ENABLE) -+ , &dev->pci->pcimstctl); -+ /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ -+ pci_set_master (pdev); -+ pci_set_mwi (pdev); -+ -+ /* ... also flushes any posted pci writes */ -+ dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; -+ -+ /* done */ -+ pci_set_drvdata (pdev, dev); -+ INFO (dev, "%s\n", driver_desc); -+ INFO (dev, "irq %s, pci mem %p, chip rev %04x\n", -+ bufp, base, dev->chiprev); -+ INFO (dev, "version: " DRIVER_VERSION "; dma %s\n", -+ use_dma -+ ? (use_dma_chaining ? "chaining" : "enabled") -+ : "disabled"); -+ the_controller = dev; -+ -+ return 0; -+ -+done: -+ if (dev) -+ net2280_remove (pdev); -+ return retval; -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct pci_device_id pci_ids [] = { { -+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), -+ .class_mask = ~0, -+ .vendor = 0x17cc, -+ .device = 0x2280, -+ .subvendor = PCI_ANY_ID, -+ .subdevice = PCI_ANY_ID, -+ -+}, { /* end: all zeroes */ } -+}; -+MODULE_DEVICE_TABLE (pci, pci_ids); -+ -+/* pci driver glue; this is a "new style" PCI driver module */ -+static struct pci_driver net2280_pci_driver = { -+ .name = (char *) driver_name, -+ .id_table = pci_ids, -+ -+ .probe = net2280_probe, -+ .remove = net2280_remove, -+ -+ /* FIXME add power management support */ -+}; -+ -+MODULE_DESCRIPTION (DRIVER_DESC); -+MODULE_AUTHOR ("David Brownell"); -+MODULE_LICENSE ("GPL"); -+ -+static int __init init (void) -+{ -+ if (!use_dma) -+ use_dma_chaining = 0; -+ return pci_module_init (&net2280_pci_driver); -+} -+module_init (init); -+ -+static void __exit cleanup (void) -+{ -+ pci_unregister_driver (&net2280_pci_driver); -+} -+module_exit (cleanup); -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/net2280.h kernel/drivers/usb/gadget/net2280.h ---- /tmp/kernel/drivers/usb/gadget/net2280.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/net2280.h 2005-04-22 17:53:19.483531538 +0200 -@@ -0,0 +1,756 @@ -+/* -+ * NetChip 2280 high/full speed USB device controller. -+ * Unlike many such controllers, this one talks PCI. -+ */ -+ -+/* -+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) -+ * Copyright (C) 2003 David Brownell -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* NET2280 MEMORY MAPPED REGISTERS -+ * -+ * The register layout came from the chip documentation, and the bit -+ * number definitions were extracted from chip specification. -+ * -+ * Use the shift operator ('<<') to build bit masks, with readl/writel -+ * to access the registers through PCI. -+ */ -+ -+/* main registers, BAR0 + 0x0000 */ -+struct net2280_regs { -+ // offset 0x0000 -+ u32 devinit; -+#define LOCAL_CLOCK_FREQUENCY 8 -+#define FORCE_PCI_RESET 7 -+#define PCI_ID 6 -+#define PCI_ENABLE 5 -+#define FIFO_SOFT_RESET 4 -+#define CFG_SOFT_RESET 3 -+#define PCI_SOFT_RESET 2 -+#define USB_SOFT_RESET 1 -+#define M8051_RESET 0 -+ u32 eectl; -+#define EEPROM_ADDRESS_WIDTH 23 -+#define EEPROM_CHIP_SELECT_ACTIVE 22 -+#define EEPROM_PRESENT 21 -+#define EEPROM_VALID 20 -+#define EEPROM_BUSY 19 -+#define EEPROM_CHIP_SELECT_ENABLE 18 -+#define EEPROM_BYTE_READ_START 17 -+#define EEPROM_BYTE_WRITE_START 16 -+#define EEPROM_READ_DATA 8 -+#define EEPROM_WRITE_DATA 0 -+ u32 eeclkfreq; -+ u32 _unused0; -+ // offset 0x0010 -+ -+ u32 pciirqenb0; /* interrupt PCI master ... */ -+#define SETUP_PACKET_INTERRUPT_ENABLE 7 -+#define ENDPOINT_F_INTERRUPT_ENABLE 6 -+#define ENDPOINT_E_INTERRUPT_ENABLE 5 -+#define ENDPOINT_D_INTERRUPT_ENABLE 4 -+#define ENDPOINT_C_INTERRUPT_ENABLE 3 -+#define ENDPOINT_B_INTERRUPT_ENABLE 2 -+#define ENDPOINT_A_INTERRUPT_ENABLE 1 -+#define ENDPOINT_0_INTERRUPT_ENABLE 0 -+ u32 pciirqenb1; -+#define PCI_INTERRUPT_ENABLE 31 -+#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 -+#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 -+#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 -+#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 -+#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 -+#define PCI_TARGET_ABORT_ASSERTED_INTERRUPT_ENABLE 18 -+#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 -+#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 -+#define GPIO_INTERRUPT_ENABLE 13 -+#define DMA_D_INTERRUPT_ENABLE 12 -+#define DMA_C_INTERRUPT_ENABLE 11 -+#define DMA_B_INTERRUPT_ENABLE 10 -+#define DMA_A_INTERRUPT_ENABLE 9 -+#define EEPROM_DONE_INTERRUPT_ENABLE 8 -+#define VBUS_INTERRUPT_ENABLE 7 -+#define CONTROL_STATUS_INTERRUPT_ENABLE 6 -+#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 -+#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 -+#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 -+#define RESUME_INTERRUPT_ENABLE 1 -+#define SOF_INTERRUPT_ENABLE 0 -+ u32 cpu_irqenb0; /* ... or onboard 8051 */ -+#define SETUP_PACKET_INTERRUPT_ENABLE 7 -+#define ENDPOINT_F_INTERRUPT_ENABLE 6 -+#define ENDPOINT_E_INTERRUPT_ENABLE 5 -+#define ENDPOINT_D_INTERRUPT_ENABLE 4 -+#define ENDPOINT_C_INTERRUPT_ENABLE 3 -+#define ENDPOINT_B_INTERRUPT_ENABLE 2 -+#define ENDPOINT_A_INTERRUPT_ENABLE 1 -+#define ENDPOINT_0_INTERRUPT_ENABLE 0 -+ u32 cpu_irqenb1; -+#define CPU_INTERRUPT_ENABLE 31 -+#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 -+#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 -+#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 -+#define PCI_INTA_INTERRUPT_ENABLE 24 -+#define PCI_PME_INTERRUPT_ENABLE 23 -+#define PCI_SERR_INTERRUPT_ENABLE 22 -+#define PCI_PERR_INTERRUPT_ENABLE 21 -+#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 -+#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 -+#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 -+#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 -+#define GPIO_INTERRUPT_ENABLE 13 -+#define DMA_D_INTERRUPT_ENABLE 12 -+#define DMA_C_INTERRUPT_ENABLE 11 -+#define DMA_B_INTERRUPT_ENABLE 10 -+#define DMA_A_INTERRUPT_ENABLE 9 -+#define EEPROM_DONE_INTERRUPT_ENABLE 8 -+#define VBUS_INTERRUPT_ENABLE 7 -+#define CONTROL_STATUS_INTERRUPT_ENABLE 6 -+#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 -+#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 -+#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 -+#define RESUME_INTERRUPT_ENABLE 1 -+#define SOF_INTERRUPT_ENABLE 0 -+ -+ // offset 0x0020 -+ u32 _unused1; -+ u32 usbirqenb1; -+#define USB_INTERRUPT_ENABLE 31 -+#define POWER_STATE_CHANGE_INTERRUPT_ENABLE 27 -+#define PCI_ARBITER_TIMEOUT_INTERRUPT_ENABLE 26 -+#define PCI_PARITY_ERROR_INTERRUPT_ENABLE 25 -+#define PCI_INTA_INTERRUPT_ENABLE 24 -+#define PCI_PME_INTERRUPT_ENABLE 23 -+#define PCI_SERR_INTERRUPT_ENABLE 22 -+#define PCI_PERR_INTERRUPT_ENABLE 21 -+#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE 20 -+#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE 19 -+#define PCI_RETRY_ABORT_INTERRUPT_ENABLE 17 -+#define PCI_MASTER_CYCLE_DONE_INTERRUPT_ENABLE 16 -+#define GPIO_INTERRUPT_ENABLE 13 -+#define DMA_D_INTERRUPT_ENABLE 12 -+#define DMA_C_INTERRUPT_ENABLE 11 -+#define DMA_B_INTERRUPT_ENABLE 10 -+#define DMA_A_INTERRUPT_ENABLE 9 -+#define EEPROM_DONE_INTERRUPT_ENABLE 8 -+#define VBUS_INTERRUPT_ENABLE 7 -+#define CONTROL_STATUS_INTERRUPT_ENABLE 6 -+#define ROOT_PORT_RESET_INTERRUPT_ENABLE 4 -+#define SUSPEND_REQUEST_INTERRUPT_ENABLE 3 -+#define SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE 2 -+#define RESUME_INTERRUPT_ENABLE 1 -+#define SOF_INTERRUPT_ENABLE 0 -+ u32 irqstat0; -+#define INTA_ASSERTED 12 -+#define SETUP_PACKET_INTERRUPT 7 -+#define ENDPOINT_F_INTERRUPT 6 -+#define ENDPOINT_E_INTERRUPT 5 -+#define ENDPOINT_D_INTERRUPT 4 -+#define ENDPOINT_C_INTERRUPT 3 -+#define ENDPOINT_B_INTERRUPT 2 -+#define ENDPOINT_A_INTERRUPT 1 -+#define ENDPOINT_0_INTERRUPT 0 -+ u32 irqstat1; -+#define POWER_STATE_CHANGE_INTERRUPT 27 -+#define PCI_ARBITER_TIMEOUT_INTERRUPT 26 -+#define PCI_PARITY_ERROR_INTERRUPT 25 -+#define PCI_INTA_INTERRUPT 24 -+#define PCI_PME_INTERRUPT 23 -+#define PCI_SERR_INTERRUPT 22 -+#define PCI_PERR_INTERRUPT 21 -+#define PCI_MASTER_ABORT_RECEIVED_INTERRUPT 20 -+#define PCI_TARGET_ABORT_RECEIVED_INTERRUPT 19 -+#define PCI_RETRY_ABORT_INTERRUPT 17 -+#define PCI_MASTER_CYCLE_DONE_INTERRUPT 16 -+#define GPIO_INTERRUPT 13 -+#define DMA_D_INTERRUPT 12 -+#define DMA_C_INTERRUPT 11 -+#define DMA_B_INTERRUPT 10 -+#define DMA_A_INTERRUPT 9 -+#define EEPROM_DONE_INTERRUPT 8 -+#define VBUS_INTERRUPT 7 -+#define CONTROL_STATUS_INTERRUPT 6 -+#define ROOT_PORT_RESET_INTERRUPT 4 -+#define SUSPEND_REQUEST_INTERRUPT 3 -+#define SUSPEND_REQUEST_CHANGE_INTERRUPT 2 -+#define RESUME_INTERRUPT 1 -+#define SOF_INTERRUPT 0 -+ // offset 0x0030 -+ u32 idxaddr; -+ u32 idxdata; -+ u32 fifoctl; -+#define PCI_BASE2_RANGE 16 -+#define IGNORE_FIFO_AVAILABILITY 3 -+#define PCI_BASE2_SELECT 2 -+#define FIFO_CONFIGURATION_SELECT 0 -+ u32 _unused2; -+ // offset 0x0040 -+ u32 memaddr; -+#define START 28 -+#define DIRECTION 27 -+#define FIFO_DIAGNOSTIC_SELECT 24 -+#define MEMORY_ADDRESS 0 -+ u32 memdata0; -+ u32 memdata1; -+ u32 _unused3; -+ // offset 0x0050 -+ u32 gpioctl; -+#define GPIO3_LED_SELECT 12 -+#define GPIO3_INTERRUPT_ENABLE 11 -+#define GPIO2_INTERRUPT_ENABLE 10 -+#define GPIO1_INTERRUPT_ENABLE 9 -+#define GPIO0_INTERRUPT_ENABLE 8 -+#define GPIO3_OUTPUT_ENABLE 7 -+#define GPIO2_OUTPUT_ENABLE 6 -+#define GPIO1_OUTPUT_ENABLE 5 -+#define GPIO0_OUTPUT_ENABLE 4 -+#define GPIO3_DATA 3 -+#define GPIO2_DATA 2 -+#define GPIO1_DATA 1 -+#define GPIO0_DATA 0 -+ u32 gpiostat; -+#define GPIO3_INTERRUPT 3 -+#define GPIO2_INTERRUPT 2 -+#define GPIO1_INTERRUPT 1 -+#define GPIO0_INTERRUPT 0 -+} __attribute__ ((packed)); -+ -+/* usb control, BAR0 + 0x0080 */ -+struct net2280_usb_regs { -+ // offset 0x0080 -+ u32 stdrsp; -+#define STALL_UNSUPPORTED_REQUESTS 31 -+#define SET_TEST_MODE 16 -+#define GET_OTHER_SPEED_CONFIGURATION 15 -+#define GET_DEVICE_QUALIFIER 14 -+#define SET_ADDRESS 13 -+#define ENDPOINT_SET_CLEAR_HALT 12 -+#define DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP 11 -+#define GET_STRING_DESCRIPTOR_2 10 -+#define GET_STRING_DESCRIPTOR_1 9 -+#define GET_STRING_DESCRIPTOR_0 8 -+#define GET_SET_INTERFACE 6 -+#define GET_SET_CONFIGURATION 5 -+#define GET_CONFIGURATION_DESCRIPTOR 4 -+#define GET_DEVICE_DESCRIPTOR 3 -+#define GET_ENDPOINT_STATUS 2 -+#define GET_INTERFACE_STATUS 1 -+#define GET_DEVICE_STATUS 0 -+ u32 prodvendid; -+#define PRODUCT_ID 16 -+#define VENDOR_ID 0 -+ u32 relnum; -+ u32 usbctl; -+#define SERIAL_NUMBER_INDEX 16 -+#define PRODUCT_ID_STRING_ENABLE 13 -+#define VENDOR_ID_STRING_ENABLE 12 -+#define USB_ROOT_PORT_WAKEUP_ENABLE 11 -+#define VBUS_PIN 10 -+#define TIMED_DISCONNECT 9 -+#define SUSPEND_IMMEDIATELY 7 -+#define SELF_POWERED_USB_DEVICE 6 -+#define REMOTE_WAKEUP_SUPPORT 5 -+#define PME_POLARITY 4 -+#define USB_DETECT_ENABLE 3 -+#define PME_WAKEUP_ENABLE 2 -+#define DEVICE_REMOTE_WAKEUP_ENABLE 1 -+#define SELF_POWERED_STATUS 0 -+ // offset 0x0090 -+ u32 usbstat; -+#define HIGH_SPEED 7 -+#define FULL_SPEED 6 -+#define GENERATE_RESUME 5 -+#define GENERATE_DEVICE_REMOTE_WAKEUP 4 -+ u32 xcvrdiag; -+#define FORCE_HIGH_SPEED_MODE 31 -+#define FORCE_FULL_SPEED_MODE 30 -+#define USB_TEST_MODE 24 -+#define LINE_STATE 16 -+#define TRANSCEIVER_OPERATION_MODE 2 -+#define TRANSCEIVER_SELECT 1 -+#define TERMINATION_SELECT 0 -+ u32 setup0123; -+ u32 setup4567; -+ // offset 0x0090 -+ u32 _unused0; -+ u32 ouraddr; -+#define FORCE_IMMEDIATE 7 -+#define OUR_USB_ADDRESS 0 -+ u32 ourconfig; -+} __attribute__ ((packed)); -+ -+/* pci control, BAR0 + 0x0100 */ -+struct net2280_pci_regs { -+ // offset 0x0100 -+ u32 pcimstctl; -+#define PCI_ARBITER_PARK_SELECT 13 -+#define PCI_MULTI LEVEL_ARBITER 12 -+#define PCI_RETRY_ABORT_ENABLE 11 -+#define DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE 10 -+#define DMA_READ_MULTIPLE_ENABLE 9 -+#define DMA_READ_LINE_ENABLE 8 -+#define PCI_MASTER_COMMAND_SELECT 6 -+#define MEM_READ_OR_WRITE 0 -+#define IO_READ_OR_WRITE 1 -+#define CFG_READ_OR_WRITE 2 -+#define PCI_MASTER_START 5 -+#define PCI_MASTER_READ_WRITE 4 -+#define PCI_MASTER_WRITE 0 -+#define PCI_MASTER_READ 1 -+#define PCI_MASTER_BYTE_WRITE_ENABLES 0 -+ u32 pcimstaddr; -+ u32 pcimstdata; -+ u32 pcimststat; -+#define PCI_ARBITER_CLEAR 2 -+#define PCI_EXTERNAL_ARBITER 1 -+#define PCI_HOST_MODE 0 -+} __attribute__ ((packed)); -+ -+/* dma control, BAR0 + 0x0180 ... array of four structs like this, -+ * for channels 0..3. see also struct net2280_dma: descriptor -+ * that can be loaded into some of these registers. -+ */ -+struct net2280_dma_regs { /* [11.7] */ -+ // offset 0x0180, 0x01a0, 0x01c0, 0x01e0, -+ u32 dmactl; -+#define DMA_SCATTER_GATHER_DONE_INTERRUPT_ENABLE 25 -+#define DMA_CLEAR_COUNT_ENABLE 21 -+#define DESCRIPTOR_POLLING_RATE 19 -+#define POLL_CONTINUOUS 0 -+#define POLL_1_USEC 1 -+#define POLL_100_USEC 2 -+#define POLL_1_MSEC 3 -+#define DMA_VALID_BIT_POLLING_ENABLE 18 -+#define DMA_VALID_BIT_ENABLE 17 -+#define DMA_SCATTER_GATHER_ENABLE 16 -+#define DMA_OUT_AUTO_START_ENABLE 4 -+#define DMA_PREEMPT_ENABLE 3 -+#define DMA_FIFO_VALIDATE 2 -+#define DMA_ENABLE 1 -+#define DMA_ADDRESS_HOLD 0 -+ u32 dmastat; -+#define DMA_SCATTER_GATHER_DONE_INTERRUPT 25 -+#define DMA_TRANSACTION_DONE_INTERRUPT 24 -+#define DMA_ABORT 1 -+#define DMA_START 0 -+ u32 _unused0 [2]; -+ // offset 0x0190, 0x01b0, 0x01d0, 0x01f0, -+ u32 dmacount; -+#define VALID_BIT 31 -+#define DMA_DIRECTION 30 -+#define DMA_DONE_INTERRUPT_ENABLE 29 -+#define END_OF_CHAIN 28 -+#define DMA_BYTE_COUNT_MASK ((1<<24)-1) -+#define DMA_BYTE_COUNT 0 -+ u32 dmaaddr; -+ u32 dmadesc; -+ u32 _unused1; -+} __attribute__ ((packed)); -+ -+/* dedicated endpoint registers, BAR0 + 0x0200 */ -+ -+struct net2280_dep_regs { /* [11.8] */ -+ // offset 0x0200, 0x0210, 0x220, 0x230, 0x240 -+ u32 dep_cfg; -+ // offset 0x0204, 0x0214, 0x224, 0x234, 0x244 -+ u32 dep_rsp; -+ u32 _unused [2]; -+} __attribute__ ((packed)); -+ -+/* configurable endpoint registers, BAR0 + 0x0300 ... array of seven structs -+ * like this, for ep0 then the configurable endpoints A..F -+ * ep0 reserved for control; E and F have only 64 bytes of fifo -+ */ -+struct net2280_ep_regs { /* [11.9] */ -+ // offset 0x0300, 0x0320, 0x0340, 0x0360, 0x0380, 0x03a0, 0x03c0 -+ u32 ep_cfg; -+#define ENDPOINT_BYTE_COUNT 16 -+#define ENDPOINT_ENABLE 10 -+#define ENDPOINT_TYPE 8 -+#define ENDPOINT_DIRECTION 7 -+#define ENDPOINT_NUMBER 0 -+ u32 ep_rsp; -+#define SET_NAK_OUT_PACKETS 15 -+#define SET_EP_HIDE_STATUS_PHASE 14 -+#define SET_EP_FORCE_CRC_ERROR 13 -+#define SET_INTERRUPT_MODE 12 -+#define SET_CONTROL_STATUS_PHASE_HANDSHAKE 11 -+#define SET_NAK_OUT_PACKETS_MODE 10 -+#define SET_ENDPOINT_TOGGLE 9 -+#define SET_ENDPOINT_HALT 8 -+#define CLEAR_NAK_OUT_PACKETS 7 -+#define CLEAR_EP_HIDE_STATUS_PHASE 6 -+#define CLEAR_EP_FORCE_CRC_ERROR 5 -+#define CLEAR_INTERRUPT_MODE 4 -+#define CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE 3 -+#define CLEAR_NAK_OUT_PACKETS_MODE 2 -+#define CLEAR_ENDPOINT_TOGGLE 1 -+#define CLEAR_ENDPOINT_HALT 0 -+ u32 ep_irqenb; -+#define SHORT_PACKET_OUT_DONE_INTERRUPT_ENABLE 6 -+#define SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE 5 -+#define DATA_PACKET_RECEIVED_INTERRUPT_ENABLE 3 -+#define DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE 2 -+#define DATA_OUT_PING_TOKEN_INTERRUPT_ENABLE 1 -+#define DATA_IN_TOKEN_INTERRUPT_ENABLE 0 -+ u32 ep_stat; -+#define FIFO_VALID_COUNT 24 -+#define HIGH_BANDWIDTH_OUT_TRANSACTION_PID 22 -+#define TIMEOUT 21 -+#define USB_STALL_SENT 20 -+#define USB_IN_NAK_SENT 19 -+#define USB_IN_ACK_RCVD 18 -+#define USB_OUT_PING_NAK_SENT 17 -+#define USB_OUT_ACK_SENT 16 -+#define FIFO_OVERFLOW 13 -+#define FIFO_UNDERFLOW 12 -+#define FIFO_FULL 11 -+#define FIFO_EMPTY 10 -+#define FIFO_FLUSH 9 -+#define SHORT_PACKET_OUT_DONE_INTERRUPT 6 -+#define SHORT_PACKET_TRANSFERRED_INTERRUPT 5 -+#define NAK_OUT_PACKETS 4 -+#define DATA_PACKET_RECEIVED_INTERRUPT 3 -+#define DATA_PACKET_TRANSMITTED_INTERRUPT 2 -+#define DATA_OUT_PING_TOKEN_INTERRUPT 1 -+#define DATA_IN_TOKEN_INTERRUPT 0 -+ // offset 0x0310, 0x0330, 0x0350, 0x0370, 0x0390, 0x03b0, 0x03d0 -+ u32 ep_avail; -+ u32 ep_data; -+ u32 _unused0 [2]; -+} __attribute__ ((packed)); -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef __KERNEL__ -+ -+/* indexed registers [11.10] are accessed indirectly -+ * caller must own the device lock. -+ */ -+ -+static inline u32 -+get_idx_reg (struct net2280_regs *regs, u32 index) -+{ -+ writel (index, ®s->idxaddr); -+ /* NOTE: synchs device/cpu memory views */ -+ return readl (®s->idxdata); -+} -+ -+static inline void -+set_idx_reg (struct net2280_regs *regs, u32 index, u32 value) -+{ -+ writel (index, ®s->idxaddr); -+ writel (value, ®s->idxdata); -+ /* posted, may not be visible yet */ -+} -+ -+#endif /* __KERNEL__ */ -+ -+ -+#define REG_DIAG 0x0 -+#define RETRY_COUNTER 16 -+#define FORCE_PCI_SERR 11 -+#define FORCE_PCI_INTERRUPT 10 -+#define FORCE_USB_INTERRUPT 9 -+#define FORCE_CPU_INTERRUPT 8 -+#define ILLEGAL_BYTE_ENABLES 5 -+#define FAST_TIMES 4 -+#define FORCE_RECEIVE_ERROR 2 -+#define FORCE_TRANSMIT_CRC_ERROR 0 -+#define REG_FRAME 0x02 /* from last sof */ -+#define REG_CHIPREV 0x03 /* in bcd */ -+#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */ -+ -+#define CHIPREV_1 0x0100 -+#define CHIPREV_1A 0x0110 -+ -+#ifdef __KERNEL__ -+ -+/* ep a-f highspeed and fullspeed maxpacket, addresses -+ * computed from ep->num -+ */ -+#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \ -+ (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1)) -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* [8.3] for scatter/gather i/o -+ * use struct net2280_dma_regs bitfields -+ */ -+struct net2280_dma { -+ u32 dmacount; -+ u32 dmaaddr; /* the buffer */ -+ u32 dmadesc; /* next dma descriptor */ -+ u32 _reserved; -+} __attribute__ ((aligned (16))); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* DRIVER DATA STRUCTURES and UTILITIES */ -+ -+struct net2280_ep { -+ struct usb_ep ep; -+ struct net2280_ep_regs *regs; -+ struct net2280_dma_regs *dma; -+ struct net2280_dma *dummy; -+ dma_addr_t td_dma; /* of dummy */ -+ struct net2280 *dev; -+ unsigned long irqs; -+ -+ /* analogous to a host-side qh */ -+ struct list_head queue; -+ const struct usb_endpoint_descriptor *desc; -+ unsigned num : 8, -+ fifo_size : 12, -+ in_fifo_validate : 1, -+ out_overflow : 1, -+ stopped : 1, -+ is_in : 1, -+ is_iso : 1; -+}; -+ -+static inline void allow_status (struct net2280_ep *ep) -+{ -+ /* ep0 only */ -+ writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) -+ | (1 << CLEAR_NAK_OUT_PACKETS) -+ | (1 << CLEAR_NAK_OUT_PACKETS_MODE) -+ , &ep->regs->ep_rsp); -+ ep->stopped = 1; -+} -+ -+/* count (<= 4) bytes in the next fifo write will be valid */ -+static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count) -+{ -+ writeb (count, 2 + (u8 *) &ep->regs->ep_cfg); -+} -+ -+struct net2280_request { -+ struct usb_request req; -+ struct net2280_dma *td; -+ dma_addr_t td_dma; -+ struct list_head queue; -+ unsigned mapped : 1, -+ valid : 1; -+}; -+ -+struct net2280 { -+ /* each pci device provides one gadget, several endpoints */ -+ struct usb_gadget gadget; -+ spinlock_t lock; -+ struct net2280_ep ep [7]; -+ struct usb_gadget_driver *driver; -+ unsigned enabled : 1, -+ protocol_stall : 1, -+ softconnect : 1, -+ got_irq : 1, -+ region : 1; -+ u16 chiprev; -+ -+ /* pci state used to access those endpoints */ -+ struct pci_dev *pdev; -+ struct net2280_regs *regs; -+ struct net2280_usb_regs *usb; -+ struct net2280_pci_regs *pci; -+ struct net2280_dma_regs *dma; -+ struct net2280_dep_regs *dep; -+ struct net2280_ep_regs *epregs; -+ -+ struct pci_pool *requests; -+ // statistics... -+}; -+ -+static inline void set_halt (struct net2280_ep *ep) -+{ -+ /* ep0 and bulk/intr endpoints */ -+ writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) -+ /* set NAK_OUT for erratum 0114 */ -+ | ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) -+ | (1 << SET_ENDPOINT_HALT) -+ , &ep->regs->ep_rsp); -+} -+ -+static inline void clear_halt (struct net2280_ep *ep) -+{ -+ /* ep0 and bulk/intr endpoints */ -+ writel ( (1 << CLEAR_ENDPOINT_HALT) -+ | (1 << CLEAR_ENDPOINT_TOGGLE) -+ /* unless the gadget driver left a short packet in the -+ * fifo, this reverses the erratum 0114 workaround. -+ */ -+ | ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS) -+ , &ep->regs->ep_rsp); -+} -+ -+#ifdef USE_RDK_LEDS -+ -+static inline void net2280_led_init (struct net2280 *dev) -+{ -+ /* LED3 (green) is on during USB activity. note erratum 0113. */ -+ writel ((1 << GPIO3_LED_SELECT) -+ | (1 << GPIO3_OUTPUT_ENABLE) -+ | (1 << GPIO2_OUTPUT_ENABLE) -+ | (1 << GPIO1_OUTPUT_ENABLE) -+ | (1 << GPIO0_OUTPUT_ENABLE) -+ , &dev->regs->gpioctl); -+} -+ -+/* indicate speed with bi-color LED 0/1 */ -+static inline -+void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed) -+{ -+ u32 val = readl (&dev->regs->gpioctl); -+ switch (speed) { -+ case USB_SPEED_HIGH: /* green */ -+ val &= ~(1 << GPIO0_DATA); -+ val |= (1 << GPIO1_DATA); -+ break; -+ case USB_SPEED_FULL: /* red */ -+ val &= ~(1 << GPIO1_DATA); -+ val |= (1 << GPIO0_DATA); -+ break; -+ default: /* (off/black) */ -+ val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA)); -+ break; -+ } -+ writel (val, &dev->regs->gpioctl); -+} -+ -+/* indicate power with LED 2 */ -+static inline void net2280_led_active (struct net2280 *dev, int is_active) -+{ -+ u32 val = readl (&dev->regs->gpioctl); -+ -+ // FIXME this LED never seems to turn on. -+ if (is_active) -+ val |= GPIO2_DATA; -+ else -+ val &= ~GPIO2_DATA; -+ writel (val, &dev->regs->gpioctl); -+} -+static inline void net2280_led_shutdown (struct net2280 *dev) -+{ -+ /* turn off all four GPIO*_DATA bits */ -+ writel (readl (&dev->regs->gpioctl) & ~0x0f, -+ &dev->regs->gpioctl); -+} -+ -+#else -+ -+#define net2280_led_init(dev) do { } while (0) -+#define net2280_led_speed(dev, speed) do { } while (0) -+#define net2280_led_shutdown(dev) do { } while (0) -+ -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define xprintk(dev,level,fmt,args...) \ -+ printk(level "%s %s: " fmt , driver_name , \ -+ dev->pdev->slot_name , ## args) -+ -+#ifdef DEBUG -+#undef DEBUG -+#define DEBUG(dev,fmt,args...) \ -+ xprintk(dev , KERN_DEBUG , fmt , ## args) -+#else -+#define DEBUG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+#ifdef VERBOSE -+#define VDEBUG DEBUG -+#else -+#define VDEBUG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* VERBOSE */ -+ -+#define ERROR(dev,fmt,args...) \ -+ xprintk(dev , KERN_ERR , fmt , ## args) -+#define WARN(dev,fmt,args...) \ -+ xprintk(dev , KERN_WARNING , fmt , ## args) -+#define INFO(dev,fmt,args...) \ -+ xprintk(dev , KERN_INFO , fmt , ## args) -+ -+/*-------------------------------------------------------------------------*/ -+ -+static inline void start_out_naking (struct net2280_ep *ep) -+{ -+ /* NOTE: hardware races lurk here, and PING protocol issues */ -+ writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); -+ /* synch with device */ -+ readl (&ep->regs->ep_rsp); -+} -+ -+#ifdef DEBUG -+static inline void assert_out_naking (struct net2280_ep *ep, const char *where) -+{ -+ u32 tmp = readl (&ep->regs->ep_stat); -+ -+ if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { -+ DEBUG (ep->dev, "%s %s %08x !NAK\n", -+ ep->ep.name, where, tmp); -+ writel ((1 << SET_NAK_OUT_PACKETS), -+ &ep->regs->ep_rsp); -+ } -+} -+#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__FUNCTION__) -+#else -+#define ASSERT_OUT_NAKING(ep) do {} while (0) -+#endif -+ -+static inline void stop_out_naking (struct net2280_ep *ep) -+{ -+ u32 tmp; -+ -+ tmp = readl (&ep->regs->ep_stat); -+ if ((tmp & (1 << NAK_OUT_PACKETS)) != 0) -+ writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* 2.5 and 2.4.older portability changes ... */ -+ -+#ifndef container_of -+#define container_of list_entry -+#endif -+ -+#ifndef likely -+#define likely(x) (x) -+#define unlikely(x) (x) -+#endif -+ -+#ifndef BUG_ON -+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) -+#endif -+ -+#ifndef WARN_ON -+#define WARN_ON(x) do { } while (0) -+#endif -+ -+#ifndef IRQ_NONE -+typedef void irqreturn_t; -+#define IRQ_NONE -+#define IRQ_HANDLED -+#define IRQ_RETVAL(x) -+#endif -+ -+#endif /* __KERNEL__ */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/pxa2xx_udc.c kernel/drivers/usb/gadget/pxa2xx_udc.c ---- /tmp/kernel/drivers/usb/gadget/pxa2xx_udc.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/pxa2xx_udc.c 2005-04-22 17:53:19.492530073 +0200 -@@ -0,0 +1,2486 @@ -+/* -+ * linux/drivers/usb/gadget/pxa2xx_udc.c -+ * Intel PXA2xx and IXP4xx on-chip full speed USB device controllers -+ * -+ * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) -+ * Copyright (C) 2003 Robert Schwebel, Pengutronix -+ * Copyright (C) 2003 Benedikt Spranger, Pengutronix -+ * Copyright (C) 2003 David Brownell -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ * -+ */ -+ -+#define DEBUG 1 -+// #define VERBOSE DBG_VERBOSE -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/ioport.h> -+#include <linux/types.h> -+#include <linux/version.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+#include <linux/proc_fs.h> -+#include <linux/mm.h> -+// #include <linux/device.h> -+ -+#include <asm/byteorder.h> -+#include <asm/dma.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/unaligned.h> -+#include <asm/proc/cache.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+ -+/* -+ * This driver handles the USB Device Controller (UDC) in Intel's PXA 2xx -+ * series processors. The UDC for the IXP 4xx series is very similar. -+ * There are fifteen endpoints, in addition to ep0. -+ * -+ * Such controller drivers work with a gadget driver. The gadget driver -+ * returns descriptors, implements configuration and data protocols used -+ * by the host to interact with this device, and allocates endpoints to -+ * the different protocol interfaces. The controller driver virtualizes -+ * usb hardware so that the gadget drivers will be more portable. -+ * -+ * This UDC hardware wants to implement a bit too much USB protocol, so -+ * it constrains the sorts of USB configuration change events that work. -+ * The errata for these chips are misleading; some "fixed" bugs from -+ * pxa250 a0/a1 b0/b1/b2 sure act like they're still there. -+ */ -+ -+/* NOTE: the 2.6 driver is probably the most current version */ -+#define DRIVER_VERSION "5-Jan-2004" -+#define DRIVER_DESC "PXA 2xx USB Device Controller driver" -+ -+static const char driver_name [] = "pxa2xx_udc"; -+ -+static const char ep0name [] = "ep0"; -+ -+ -+// #define USE_DMA -+// #define USE_OUT_DMA -+// #define DISABLE_TEST_MODE -+ -+#ifdef CONFIG_PROC_FS -+#define UDC_PROC_FILE -+#endif -+ -+#ifdef CONFIG_ARCH_IXP425 -+#undef USE_DMA -+ -+/* cpu-specific register addresses are compiled in to this code */ -+#ifdef CONFIG_ARCH_PXA -+#error "Can't configure both IXP and PXA" -+#endif -+ -+#endif -+ -+#ifdef CONFIG_EMBEDDED -+/* few strings, and little code to use them */ -+#undef DEBUG -+#undef UDC_PROC_FILE -+#endif -+ -+ -+#include "pxa2xx_udc.h" -+ -+#ifdef USE_DMA -+static int use_dma = 1; -+MODULE_PARM (use_dma, "i"); -+MODULE_PARM_DESC (use_dma, "true to use dma"); -+ -+static void dma_nodesc_handler (int dmach, void *_ep, struct pt_regs *r); -+static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req); -+ -+#ifdef USE_OUT_DMA -+#define DMASTR " (dma support)" -+#else -+#define DMASTR " (dma in)" -+#endif -+ -+#else /* !USE_DMA */ -+#define DMASTR " (pio only)" -+#undef USE_OUT_DMA -+#endif -+ -+#ifdef CONFIG_USB_PXA2XX_SMALL -+#define SIZE_STR " (small)" -+#else -+#define SIZE_STR "" -+#endif -+ -+#ifdef DISABLE_TEST_MODE -+/* (mode == 0) == no undocumented chip tweaks -+ * (mode & 1) == double buffer bulk IN -+ * (mode & 2) == double buffer bulk OUT -+ * ... so mode = 3 (or 7, 15, etc) does it for both -+ */ -+static ushort fifo_mode = 0; -+MODULE_PARM (fifo_mode, "h"); -+MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode"); -+#endif -+ -+/* --------------------------------------------------------------------------- -+ * endpoint related parts of the api to the usb controller hardware, -+ * used by gadget driver; and the inner talker-to-hardware core. -+ * --------------------------------------------------------------------------- -+ */ -+ -+static void pxa2xx_ep_fifo_flush (struct usb_ep *ep); -+static void nuke (struct pxa2xx_ep *, int status); -+ -+static void pio_irq_enable(int bEndpointAddress) -+{ -+ bEndpointAddress &= 0xf; -+ if (bEndpointAddress < 8) -+ UICR0 &= ~(1 << bEndpointAddress); -+ else { -+ bEndpointAddress -= 8; -+ UICR1 &= ~(1 << bEndpointAddress); -+ } -+} -+ -+static void pio_irq_disable(int bEndpointAddress) -+{ -+ bEndpointAddress &= 0xf; -+ if (bEndpointAddress < 8) -+ UICR0 |= 1 << bEndpointAddress; -+ else { -+ bEndpointAddress -= 8; -+ UICR1 |= 1 << bEndpointAddress; -+ } -+} -+ -+/* The UDCCR reg contains mask and interrupt status bits, -+ * so using '|=' isn't safe as it may ack an interrupt. -+ */ -+#define UDCCR_MASK_BITS (UDCCR_REM | UDCCR_SRM | UDCCR_UDE) -+ -+static inline void udc_set_mask_UDCCR(int mask) -+{ -+ UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); -+} -+ -+static inline void udc_clear_mask_UDCCR(int mask) -+{ -+ UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); -+} -+ -+static inline void udc_ack_int_UDCCR(int mask) -+{ -+ /* udccr contains the bits we dont want to change */ -+ __u32 udccr = UDCCR & UDCCR_MASK_BITS; -+ -+ UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); -+} -+ -+/* -+ * endpoint enable/disable -+ * -+ * we need to verify the descriptors used to enable endpoints. since pxa2xx -+ * endpoint configurations are fixed, and are pretty much always enabled, -+ * there's not a lot to manage here. -+ * -+ * because pxa2xx can't selectively initialize bulk (or interrupt) endpoints, -+ * (resetting endpoint halt and toggle), SET_INTERFACE is unusable except -+ * for a single interface (with only the default altsetting) and for gadget -+ * drivers that don't halt endpoints (not reset by set_interface). that also -+ * means that if you use ISO, you must violate the USB spec rule that all -+ * iso endpoints must be in non-default altsettings. -+ */ -+static int pxa2xx_ep_enable (struct usb_ep *_ep, -+ const struct usb_endpoint_descriptor *desc) -+{ -+ struct pxa2xx_ep *ep; -+ struct pxa2xx_udc *dev; -+ -+ ep = container_of (_ep, struct pxa2xx_ep, ep); -+ if (!_ep || !desc || ep->desc || _ep->name == ep0name -+ || desc->bDescriptorType != USB_DT_ENDPOINT -+ || ep->bEndpointAddress != desc->bEndpointAddress -+ || ep->fifo_size < le16_to_cpu -+ (desc->wMaxPacketSize)) { -+ DMSG("%s, bad ep or descriptor\n", __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ /* xfer types must match, except that interrupt ~= bulk */ -+ if (ep->bmAttributes != desc->bmAttributes -+ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK -+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { -+ DMSG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); -+ return -EINVAL; -+ } -+ -+ /* hardware _could_ do smaller, but driver doesn't */ -+ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK -+ && le16_to_cpu (desc->wMaxPacketSize) -+ != BULK_FIFO_SIZE) -+ || !desc->wMaxPacketSize) { -+ DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); -+ return -ERANGE; -+ } -+ -+ dev = ep->dev; -+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { -+ DMSG("%s, bogus device state\n", __FUNCTION__); -+ return -ESHUTDOWN; -+ } -+ -+ ep->desc = desc; -+ ep->dma = -1; -+ ep->stopped = 0; -+ ep->pio_irqs = ep->dma_irqs = 0; -+ ep->ep.maxpacket = le16_to_cpu (desc->wMaxPacketSize); -+ -+ /* flush fifo (mostly for OUT buffers) */ -+ pxa2xx_ep_fifo_flush (_ep); -+ -+ /* ... reset halt state too, if we could ... */ -+ -+#ifdef USE_DMA -+ /* for (some) bulk and ISO endpoints, try to get a DMA channel and -+ * bind it to the endpoint. otherwise use PIO. -+ */ -+ switch (ep->bmAttributes) { -+ case USB_ENDPOINT_XFER_ISOC: -+ if (le16_to_cpu(desc->wMaxPacketSize) % 32) -+ break; -+ // fall through -+ case USB_ENDPOINT_XFER_BULK: -+ if (!use_dma || !ep->reg_drcmr) -+ break; -+ /* no bulk-out dma yet (pointless w/o descriptors) */ -+ if ((ep->bmAttributes == USB_ENDPOINT_XFER_BULK) -+ && (ep->bEndpointAddress & USB_DIR_IN) == 0) { -+ DMSG("%s dma-out NYI\n", _ep->name); -+ break; -+ } -+ ep->dma = pxa_request_dma ((char *)_ep->name, -+ (le16_to_cpu(desc->wMaxPacketSize) > 64) -+ ? DMA_PRIO_MEDIUM /* some iso */ -+ : DMA_PRIO_LOW, -+ // FIXME or ep_out_dma .. .. -+ dma_nodesc_handler, ep); -+ if (ep->dma >= 0) { -+ *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma; -+ DMSG("%s using dma%d\n", _ep->name, ep->dma); -+ } -+ } -+#endif -+ -+ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); -+ return 0; -+} -+ -+static int pxa2xx_ep_disable (struct usb_ep *_ep) -+{ -+ struct pxa2xx_ep *ep; -+ -+ ep = container_of (_ep, struct pxa2xx_ep, ep); -+ if (!_ep || !ep->desc) { -+ DMSG("%s, %s not enabled\n", __FUNCTION__, -+ _ep ? ep->ep.name : NULL); -+ return -EINVAL; -+ } -+ nuke (ep, -ESHUTDOWN); -+ -+#ifdef USE_DMA -+ if (ep->dma >= 0) { -+ *ep->reg_drcmr = 0; -+ pxa_free_dma (ep->dma); -+ ep->dma = -1; -+ } -+#endif -+ -+ /* flush fifo (mostly for IN buffers) */ -+ pxa2xx_ep_fifo_flush (_ep); -+ -+ ep->desc = 0; -+ ep->stopped = 1; -+ -+ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); -+ return 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* for the pxa2xx, these can just wrap kmalloc/kfree. gadget drivers -+ * must still pass correctly initialized endpoints, since other controller -+ * drivers may care about how it's currently set up (dma issues etc). -+ */ -+ -+/* -+ * pxa2xx_ep_alloc_request - allocate a request data structure -+ */ -+static struct usb_request * -+pxa2xx_ep_alloc_request (struct usb_ep *_ep, int gfp_flags) -+{ -+ struct pxa2xx_request *req; -+ -+ /* FIXME for bulk out-dma endpoints, preallocate a frame's worth of -+ * (aligned) dma descriptors at the end of the request -+ */ -+ -+ req = kmalloc (sizeof *req, gfp_flags); -+ if (!req) -+ return 0; -+ -+ memset (req, 0, sizeof *req); -+ INIT_LIST_HEAD (&req->queue); -+ return &req->req; -+} -+ -+ -+/* -+ * pxa2xx_ep_free_request - deallocate a request data structure -+ */ -+static void -+pxa2xx_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct pxa2xx_request *req; -+ -+ req = container_of (_req, struct pxa2xx_request, req); -+ WARN_ON (!list_empty (&req->queue)); -+ kfree(req); -+} -+ -+ -+/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's -+ * no device-affinity and the heap works perfectly well for i/o buffers. -+ */ -+static void * -+pxa2xx_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, -+ dma_addr_t *dma, int gfp_flags) -+{ -+ char *retval; -+ -+ retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); -+ if (retval) -+ *dma = virt_to_bus (retval); -+ return retval; -+} -+ -+static void -+pxa2xx_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, -+ unsigned bytes) -+{ -+ kfree (buf); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * done - retire a request; caller blocked irqs -+ */ -+static void done(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int status) -+{ -+ unsigned stopped = ep->stopped; -+ -+ list_del_init(&req->queue); -+ -+ if (likely (req->req.status == -EINPROGRESS)) -+ req->req.status = status; -+ else -+ status = req->req.status; -+ -+ if (status && status != -ESHUTDOWN) -+ DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", -+ ep->ep.name, &req->req, status, -+ req->req.actual, req->req.length); -+ -+ /* don't modify queue heads during completion callback */ -+ ep->stopped = 1; -+ req->req.complete(&ep->ep, &req->req); -+ ep->stopped = stopped; -+} -+ -+ -+static inline void ep0_idle (struct pxa2xx_udc *dev) -+{ -+ dev->ep0state = EP0_IDLE; -+ LED_EP0_OFF; -+} -+ -+static int -+write_packet(volatile u32 *uddr, struct pxa2xx_request *req, unsigned max) -+{ -+ u8 *buf; -+ unsigned length, count; -+ -+ buf = req->req.buf + req->req.actual; -+ prefetch(buf); -+ -+ /* how big will this packet be? */ -+ length = min(req->req.length - req->req.actual, max); -+ req->req.actual += length; -+ -+ count = length; -+ while (likely(count--)) -+ *uddr = *buf++; -+ -+ return length; -+} -+ -+/* -+ * write to an IN endpoint fifo, as many packets as possible. -+ * irqs will use this to write the rest later. -+ * caller guarantees at least one packet buffer is ready (or a zlp). -+ */ -+static int -+write_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -+{ -+ unsigned max; -+ -+ max = le16_to_cpu(ep->desc->wMaxPacketSize); -+ do { -+ unsigned count; -+ int is_last, is_short; -+ -+ count = write_packet(ep->reg_uddr, req, max); -+ -+ /* last packet is usually short (or a zlp) */ -+ if (unlikely (count != max)) -+ is_last = is_short = 1; -+ else { -+ if (likely(req->req.length != req->req.actual) -+ || req->req.zero) -+ is_last = 0; -+ else -+ is_last = 1; -+ /* interrupt/iso maxpacket may not fill the fifo */ -+ is_short = unlikely (max < ep->fifo_size); -+ } -+ -+ DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", -+ ep->ep.name, count, -+ is_last ? "/L" : "", is_short ? "/S" : "", -+ req->req.length - req->req.actual, req); -+ -+ /* let loose that packet. maybe try writing another one, -+ * double buffering might work. TSP, TPC, and TFS -+ * bit values are the same for all normal IN endpoints. -+ */ -+ *ep->reg_udccs = UDCCS_BI_TPC; -+ if (is_short) -+ *ep->reg_udccs = UDCCS_BI_TSP; -+ -+ /* requests complete when all IN data is in the FIFO */ -+ if (is_last) { -+ done (ep, req, 0); -+ if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) -+ pio_irq_disable (ep->bEndpointAddress); -+#ifdef USE_DMA -+ if (unlikely(ep->dma >= 0) && !list_empty(&ep->queue)) { -+DMSG("%s pio2dma\n", ep->ep.name); -+ req = list_entry(ep->queue.next, -+ struct pxa2xx_request, queue); -+ kick_dma(ep,req); -+ return 0; -+ } -+#endif -+ return 1; -+ } -+ -+ // TODO experiment: how robust can fifo mode tweaking be? -+ // the double buffering could speed up I/O a bunch. -+ -+ } while (*ep->reg_udccs & UDCCS_BI_TFS); -+ return 0; -+} -+ -+/* caller asserts req->pending (ep0 irq status nyet cleared); starts -+ * ep0 data stage. these chips want very simple state transitions. -+ */ -+static inline -+void ep0start(struct pxa2xx_udc *dev, u32 flags, const char *tag) -+{ -+ UDCCS0 = flags|UDCCS0_SA|UDCCS0_OPR; -+ USIR0 = USIR0_IR0; -+ dev->req_pending = 0; -+ DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", -+ __FUNCTION__, tag, UDCCS0, flags); -+} -+ -+static int -+write_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -+{ -+ unsigned count; -+ int is_short; -+ -+ count = write_packet(&UDDR0, req, EP0_FIFO_SIZE); -+ ep->dev->stats.write.bytes += count; -+ -+ /* last packet "must be" short (or a zlp) */ -+ is_short = (count != EP0_FIFO_SIZE); -+ -+ DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, -+ req->req.length - req->req.actual, req); -+ -+ if (unlikely (is_short)) { -+ if (ep->dev->req_pending) -+ ep0start(ep->dev, UDCCS0_IPR, "short IN"); -+ else -+ UDCCS0 = UDCCS0_IPR; -+ -+ count = req->req.length; -+ done (ep, req, 0); -+ ep0_idle(ep->dev); -+#if 1 -+ /* This seems to get rid of lost status irqs in some cases: -+ * host responds quickly, or next request involves config -+ * change automagic, or should have been hidden, or ... -+ * -+ * FIXME get rid of all udelays possible... -+ */ -+ if (count >= EP0_FIFO_SIZE) { -+ count = 100; -+ do { -+ if ((UDCCS0 & UDCCS0_OPR) != 0) { -+ /* clear OPR, generate ack */ -+ UDCCS0 = UDCCS0_OPR; -+ break; -+ } -+ count--; -+ udelay(1); -+ } while (count); -+ } -+#endif -+ } else if (ep->dev->req_pending) -+ ep0start(ep->dev, 0, "IN"); -+ return is_short; -+} -+ -+ -+/* -+ * read_fifo - unload packet(s) from the fifo we use for usb OUT -+ * transfers and put them into the request. caller should have made -+ * sure there's at least one packet ready. -+ * -+ * returns true if the request completed because of short packet or the -+ * request buffer having filled (and maybe overran till end-of-packet). -+ */ -+static int -+read_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -+{ -+ for (;;) { -+ u32 udccs; -+ u8 *buf; -+ unsigned bufferspace, count, is_short; -+ -+ /* make sure there's a packet in the FIFO. -+ * UDCCS_{BO,IO}_RPC are all the same bit value. -+ * UDCCS_{BO,IO}_RNE are all the same bit value. -+ */ -+ udccs = *ep->reg_udccs; -+ if (unlikely ((udccs & UDCCS_BO_RPC) == 0)) -+ break; -+ buf = req->req.buf + req->req.actual; -+ prefetchw(buf); -+ bufferspace = req->req.length - req->req.actual; -+ -+ /* read all bytes from this packet */ -+ if (likely (udccs & UDCCS_BO_RNE)) { -+ count = 1 + (0x0ff & *ep->reg_ubcr); -+ req->req.actual += min (count, bufferspace); -+ } else /* zlp */ -+ count = 0; -+ is_short = (count < ep->ep.maxpacket); -+ DBG(DBG_VERY_NOISY, "read %s %02x, %d bytes%s req %p %d/%d\n", -+ ep->ep.name, udccs, count, -+ is_short ? "/S" : "", -+ req, req->req.actual, req->req.length); -+ while (likely (count-- != 0)) { -+ u8 byte = (u8) *ep->reg_uddr; -+ -+ if (unlikely (bufferspace == 0)) { -+ /* this happens when the driver's buffer -+ * is smaller than what the host sent. -+ * discard the extra data. -+ */ -+ if (req->req.status != -EOVERFLOW) -+ DMSG("%s overflow %d\n", -+ ep->ep.name, count); -+ req->req.status = -EOVERFLOW; -+ } else { -+ *buf++ = byte; -+ bufferspace--; -+ } -+ } -+ *ep->reg_udccs = UDCCS_BO_RPC; -+ /* RPC/RSP/RNE could now reflect the other packet buffer */ -+ -+ /* iso is one request per packet */ -+ if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { -+ if (udccs & UDCCS_IO_ROF) -+ req->req.status = -EHOSTUNREACH; -+ /* more like "is_done" */ -+ is_short = 1; -+ } -+ -+ /* completion */ -+ if (is_short || req->req.actual == req->req.length) { -+ done (ep, req, 0); -+ if (list_empty(&ep->queue)) -+ pio_irq_disable (ep->bEndpointAddress); -+ return 1; -+ } -+ -+ /* finished that packet. the next one may be waiting... */ -+ } -+ return 0; -+} -+ -+/* -+ * special ep0 version of the above. no UBCR0 or double buffering; status -+ * handshaking is magic. most device protocols don't need control-OUT. -+ * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other -+ * protocols do use them. -+ */ -+static int -+read_ep0_fifo (struct pxa2xx_ep *ep, struct pxa2xx_request *req) -+{ -+ u8 *buf, byte; -+ unsigned bufferspace; -+ -+ buf = req->req.buf + req->req.actual; -+ bufferspace = req->req.length - req->req.actual; -+ -+ while (UDCCS0 & UDCCS0_RNE) { -+ byte = (u8) UDDR0; -+ -+ if (unlikely (bufferspace == 0)) { -+ /* this happens when the driver's buffer -+ * is smaller than what the host sent. -+ * discard the extra data. -+ */ -+ if (req->req.status != -EOVERFLOW) -+ DMSG("%s overflow\n", ep->ep.name); -+ req->req.status = -EOVERFLOW; -+ } else { -+ *buf++ = byte; -+ req->req.actual++; -+ bufferspace--; -+ } -+ } -+ -+ UDCCS0 = UDCCS0_OPR | UDCCS0_IPR; -+ -+ /* completion */ -+ if (req->req.actual >= req->req.length) -+ return 1; -+ -+ /* finished that packet. the next one may be waiting... */ -+ return 0; -+} -+ -+#ifdef USE_DMA -+ -+static inline void -+start_dma_nodesc(struct pxa2xx_ep *ep, struct pxa2xx_request *req, int is_in) -+{ -+ u32 dcmd = req->req.length; -+ u32 buf = virt_to_bus (req->req.buf); -+ u32 fifo = io_v2p ((u32)ep->reg_uddr); -+ -+ /* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */ -+ DCSR(ep->dma) = DCSR_NODESC; -+ dcmd |= DCMD_BURST32 | DCMD_ENDIRQEN | DCMD_WIDTH1; -+ if (is_in) { -+ DSADR(ep->dma) = buf; -+ DTADR(ep->dma) = fifo; -+ dcmd |= DCMD_FLOWTRG | DCMD_INCSRCADDR; -+ } else { -+ DSADR(ep->dma) = fifo; -+ DTADR(ep->dma) = buf; -+ dcmd |= DCMD_FLOWSRC | DCMD_INCTRGADDR; -+ } -+ DCMD(ep->dma) = dcmd; -+ DCSR(ep->dma) = DCSR_RUN | DCSR_STOPIRQEN | DCSR_NODESC; -+ /* and later the dma handler gets called */ -+} -+ -+static void kick_dma(struct pxa2xx_ep *ep, struct pxa2xx_request *req) -+{ -+ if (ep->bEndpointAddress & USB_DIR_IN) { -+ /* docs imply we can't preload with pio */ -+ if ((((u32)req->req.buf) & 0x0f) != 0) { -+// VERBOSE -+ DMSG("%s bad DMA align %p\n", -+ ep->ep.name, req->req.buf); -+pio_in: -+// FIXME PIO fallback doesn't work right yet (recovery?) -+DMSG("%s dma2pio\n", ep->ep.name); -+ pio_irq_enable(ep->bEndpointAddress); -+ if ((*ep->reg_udccs & UDCCS_BI_TFS) != 0) -+ (void) write_fifo(ep, req); -+ return; -+ } -+ /* dmacount 0 means end-of-transfer */ -+ if (unlikely((req->req.length - req->req.actual) == 0)) { -+// VERBOSE -+ DMSG("%s zlp dma write...\n", ep->ep.name); -+ goto pio_in; -+ } -+ start_dma_nodesc(ep, req, USB_DIR_IN); -+ } else { -+ // if ISO, use no-descriptor DMA -+ BUG(); -+ } -+} -+ -+static void cancel_dma(struct pxa2xx_ep *ep) -+{ -+ struct pxa2xx_request *req; -+ u32 tmp; -+ -+ if (DCSR(ep->dma) == 0 || list_empty(&ep->queue)) -+ return; -+ -+ DCSR(ep->dma) = 0; -+ while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0) -+ cpu_relax(); -+ -+ req = list_entry(ep->queue.next, struct pxa2xx_request, queue); -+ tmp = DCMD(ep->dma) & DCMD_LENGTH; -+ req->req.actual = req->req.length - (tmp & DCMD_LENGTH); -+ -+ /* the last tx packet may be incomplete, so flush the fifo. -+ * FIXME correct req.actual if we can -+ */ -+ if (ep->bEndpointAddress & USB_DIR_IN) -+ *ep->reg_udccs = UDCCS_BI_FTF; -+} -+ -+static void dma_nodesc_handler(int dmach, void *_ep, struct pt_regs *r) -+{ -+ struct pxa2xx_ep *ep = _ep; -+ struct pxa2xx_request *req; -+ u32 tmp; -+ -+ req = list_entry(ep->queue.next, struct pxa2xx_request, queue); -+ -+ ep->dev->stats.irqs++; -+ HEX_DISPLAY(ep->dev->stats.irqs); -+ -+ /* ack/clear */ -+ tmp = DCSR(ep->dma); -+ DCSR(ep->dma) = tmp; -+ if ((tmp & DCSR_STOPSTATE) == 0 -+ || (DDADR(ep->dma) & DDADR_STOP) != 0) { -+ DBG(DBG_VERBOSE, "%s, dcsr %08x ddadr %08x\n", -+ ep->ep.name, DCSR(ep->dma), DDADR(ep->dma)); -+ return; -+ } -+ DCSR(ep->dma) = 0; /* clear DCSR_STOPSTATE */ -+ -+ /* wrap up the transfer, and collect status */ -+ if (unlikely(tmp & DCSR_BUSERR)) -+ req->req.status = -EIO; -+ tmp = DCMD(ep->dma); -+ req->req.actual = req->req.length - (tmp & DCMD_LENGTH); -+ tmp = 1; /* normally this is the last packet */ -+ -+ if (ep->bEndpointAddress & USB_DIR_IN) { -+ /* maybe validate final short packet */ -+ if ((ep->bmAttributes == USB_ENDPOINT_XFER_BULK -+ && req->req.actual % BULK_FIFO_SIZE) -+ || (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC -+ && req->req.actual % ISO_FIFO_SIZE)) -+ *ep->reg_udccs = UDCCS_BI_TSP /*|UDCCS_BI_TPC*/; -+ -+ /* or force a zlp, with pio ... */ -+ else if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK -+ && req->req.zero) { -+ tmp = 0; -+ } -+ // if iso, maybe report underrun (TUR) -+ } else { -+ BUG(); -+ } -+ -+ if (likely(tmp != 0)) -+ done(ep, req, 0); -+ -+ /* maybe re-activate after completion */ -+ if (ep->stopped || list_empty(&ep->queue)) -+ return; -+ req = list_entry(ep->queue.next, struct pxa2xx_request, queue); -+ kick_dma(ep, req); -+} -+ -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int -+pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) -+{ -+ struct pxa2xx_request *req; -+ struct pxa2xx_ep *ep; -+ struct pxa2xx_udc *dev; -+ unsigned long flags; -+ -+ req = container_of(_req, struct pxa2xx_request, req); -+ if (unlikely (!_req || !_req->complete || !_req->buf -+ || !list_empty(&req->queue))) { -+ DMSG("%s, bad params\n", __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ ep = container_of(_ep, struct pxa2xx_ep, ep); -+ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ dev = ep->dev; -+ if (unlikely (!dev->driver -+ || dev->gadget.speed == USB_SPEED_UNKNOWN)) { -+ DMSG("%s, bogus device state\n", __FUNCTION__); -+ return -ESHUTDOWN; -+ } -+ -+ /* iso is always one packet per request, that's the only way -+ * we can report per-packet status. that also helps with dma. -+ */ -+ if (unlikely (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC -+ && req->req.length > le16_to_cpu -+ (ep->desc->wMaxPacketSize))) -+ return -EMSGSIZE; -+ -+#ifdef USE_DMA -+ if (ep->dma >= 0) { -+ unsigned long start = (unsigned long) _req->buf; -+ -+ clean_dcache_range(start, start + _req->length); -+ /* or for USB_DIR_OUT, invalidate_dcache_range (...) */ -+ } -+#endif -+ -+ DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", -+ _ep->name, _req, _req->length, _req->buf); -+ -+ local_irq_save(flags); -+ -+ _req->status = -EINPROGRESS; -+ _req->actual = 0; -+ -+ /* kickstart this i/o queue? */ -+ if (list_empty(&ep->queue) && !ep->stopped) { -+ if (ep->desc == 0 /* ep0 */) { -+ unsigned length = _req->length; -+ -+ switch (dev->ep0state) { -+ case EP0_IN_DATA_PHASE: -+ dev->stats.write.ops++; -+ if (write_ep0_fifo(ep, req)) -+ req = 0; -+ break; -+ -+ case EP0_OUT_DATA_PHASE: -+ dev->stats.read.ops++; -+ /* messy ... */ -+ if (dev->req_config) { -+ DBG(DBG_VERBOSE, "ep0 config ack%s\n", -+ dev->has_cfr ? "" : " raced"); -+ if (dev->has_cfr) -+ UDCCFR = UDCCFR_AREN|UDCCFR_ACM; -+ done(ep, req, 0); -+ dev->ep0state = EP0_END_XFER; -+ local_irq_restore (flags); -+ return 0; -+ } -+ if (dev->req_pending) -+ ep0start(dev, UDCCS0_IPR, "OUT"); -+ if (length == 0 || ((UDCCS0 & UDCCS0_RNE) != 0 -+ && read_ep0_fifo(ep, req))) { -+ ep0_idle(dev); -+ done(ep, req, 0); -+ req = 0; -+ } -+ break; -+ -+ default: -+ DMSG("ep0 i/o, odd state %d\n", dev->ep0state); -+ local_irq_restore (flags); -+ return -EL2HLT; -+ } -+#ifdef USE_DMA -+ /* either start dma or prime pio pump */ -+ } else if (ep->dma >= 0) { -+ kick_dma(ep, req); -+#endif -+ /* can the FIFO can satisfy the request immediately? */ -+ } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 -+ && (*ep->reg_udccs & UDCCS_BI_TFS) != 0 -+ && write_fifo(ep, req)) { -+ req = 0; -+ } else if ((*ep->reg_udccs & UDCCS_BO_RFS) != 0 -+ && read_fifo(ep, req)) { -+ req = 0; -+ } -+ -+ if (likely (req && ep->desc) && ep->dma < 0) -+ pio_irq_enable(ep->bEndpointAddress); -+ } -+ -+ /* pio or dma irq handler advances the queue. */ -+ if (likely (req != 0)) -+ list_add_tail(&req->queue, &ep->queue); -+ local_irq_restore(flags); -+ -+ return 0; -+} -+ -+ -+/* -+ * nuke - dequeue ALL requests -+ */ -+static void nuke(struct pxa2xx_ep *ep, int status) -+{ -+ struct pxa2xx_request *req; -+ -+ /* called with irqs blocked */ -+#ifdef USE_DMA -+ if (ep->dma >= 0 && !ep->stopped) -+ cancel_dma(ep); -+#endif -+ while (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, -+ struct pxa2xx_request, -+ queue); -+ done(ep, req, status); -+ } -+ if (ep->desc) -+ pio_irq_disable (ep->bEndpointAddress); -+} -+ -+ -+/* dequeue JUST ONE request */ -+static int pxa2xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct pxa2xx_ep *ep; -+ struct pxa2xx_request *req; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct pxa2xx_ep, ep); -+ if (!_ep || ep->ep.name == ep0name) -+ return -EINVAL; -+ -+ local_irq_save(flags); -+ -+ /* make sure it's actually queued on this endpoint */ -+ list_for_each_entry (req, &ep->queue, queue) { -+ if (&req->req == _req) -+ break; -+ } -+ if (&req->req != _req) { -+ local_irq_restore(flags); -+ return -EINVAL; -+ } -+ -+#ifdef USE_DMA -+ if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { -+ cancel_dma(ep); -+ done(ep, req, -ECONNRESET); -+ /* restart i/o */ -+ if (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, -+ struct pxa2xx_request, queue); -+ kick_dma(ep, req); -+ } -+ } else -+#endif -+ done(ep, req, -ECONNRESET); -+ -+ local_irq_restore(flags); -+ return 0; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static int pxa2xx_ep_set_halt(struct usb_ep *_ep, int value) -+{ -+ struct pxa2xx_ep *ep; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct pxa2xx_ep, ep); -+ if (unlikely (!_ep -+ || (!ep->desc && ep->ep.name != ep0name)) -+ || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return -EINVAL; -+ } -+ if (value == 0) { -+ /* this path (reset toggle+halt) is needed to implement -+ * SET_INTERFACE on normal hardware. but it can't be -+ * done from software on the PXA UDC, and the hardware -+ * forgets to do it as part of SET_INTERFACE automagic. -+ */ -+ DMSG("only host can clear %s halt\n", _ep->name); -+ return -EROFS; -+ } -+ -+ local_irq_save(flags); -+ -+ if ((ep->bEndpointAddress & USB_DIR_IN) != 0 -+ && ((*ep->reg_udccs & UDCCS_BI_TFS) == 0 -+ || !list_empty(&ep->queue))) { -+ local_irq_restore(flags); -+ return -EAGAIN; -+ } -+ -+ /* FST bit is the same for control, bulk in, bulk out, interrupt in */ -+ *ep->reg_udccs = UDCCS_BI_FST|UDCCS_BI_FTF; -+ -+ /* ep0 needs special care */ -+ if (!ep->desc) { -+ start_watchdog(ep->dev); -+ ep->dev->req_pending = 0; -+ ep->dev->ep0state = EP0_STALL; -+ LED_EP0_OFF; -+ -+ /* and bulk/intr endpoints like dropping stalls too */ -+ } else { -+ unsigned i; -+ for (i = 0; i < 1000; i += 20) { -+ if (*ep->reg_udccs & UDCCS_BI_SST) -+ break; -+ udelay(20); -+ } -+ } -+ local_irq_restore(flags); -+ -+ DBG(DBG_VERBOSE, "%s halt\n", _ep->name); -+ return 0; -+} -+ -+static int pxa2xx_ep_fifo_status(struct usb_ep *_ep) -+{ -+ struct pxa2xx_ep *ep; -+ -+ ep = container_of(_ep, struct pxa2xx_ep, ep); -+ if (!_ep) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return -ENODEV; -+ } -+ /* pxa can't report unclaimed bytes from IN fifos */ -+ if ((ep->bEndpointAddress & USB_DIR_IN) != 0) -+ return -EOPNOTSUPP; -+ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN -+ || (*ep->reg_udccs & UDCCS_BO_RFS) == 0) -+ return 0; -+ else -+ return (*ep->reg_ubcr & 0xfff) + 1; -+} -+ -+static void pxa2xx_ep_fifo_flush(struct usb_ep *_ep) -+{ -+ struct pxa2xx_ep *ep; -+ -+ ep = container_of(_ep, struct pxa2xx_ep, ep); -+ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return; -+ } -+ -+ /* toggle and halt bits stay unchanged */ -+ -+ /* for OUT, just read and discard the FIFO contents. */ -+ if ((ep->bEndpointAddress & USB_DIR_IN) == 0) { -+ while (((*ep->reg_udccs) & UDCCS_BO_RNE) != 0) -+ (void) *ep->reg_uddr; -+ return; -+ } -+ -+ /* most IN status is the same, but ISO can't stall */ -+ *ep->reg_udccs = UDCCS_BI_TPC|UDCCS_BI_FTF|UDCCS_BI_TUR -+ | (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) -+ ? 0 : UDCCS_BI_SST; -+} -+ -+ -+static struct usb_ep_ops pxa2xx_ep_ops = { -+ .enable = pxa2xx_ep_enable, -+ .disable = pxa2xx_ep_disable, -+ -+ .alloc_request = pxa2xx_ep_alloc_request, -+ .free_request = pxa2xx_ep_free_request, -+ -+ .alloc_buffer = pxa2xx_ep_alloc_buffer, -+ .free_buffer = pxa2xx_ep_free_buffer, -+ -+ .queue = pxa2xx_ep_queue, -+ .dequeue = pxa2xx_ep_dequeue, -+ -+ .set_halt = pxa2xx_ep_set_halt, -+ .fifo_status = pxa2xx_ep_fifo_status, -+ .fifo_flush = pxa2xx_ep_fifo_flush, -+}; -+ -+ -+/* --------------------------------------------------------------------------- -+ * device-scoped parts of the api to the usb controller hardware -+ * --------------------------------------------------------------------------- -+ */ -+ -+static int pxa2xx_udc_get_frame(struct usb_gadget *_gadget) -+{ -+ return ((UFNRH & 0x07) << 8) | (UFNRL & 0xff); -+} -+ -+static int pxa2xx_udc_wakeup(struct usb_gadget *_gadget) -+{ -+ /* host may not have enabled remote wakeup */ -+ if ((UDCCS0 & UDCCS0_DRWF) == 0) -+ return -EHOSTUNREACH; -+ udc_set_mask_UDCCR(UDCCR_RSM); -+ return 0; -+} -+ -+static const struct usb_gadget_ops pxa2xx_udc_ops = { -+ .get_frame = pxa2xx_udc_get_frame, -+ .wakeup = pxa2xx_udc_wakeup, -+ // current versions must always be self-powered -+}; -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef UDC_PROC_FILE -+ -+static const char proc_node_name [] = "driver/udc"; -+ -+static int -+udc_proc_read(char *page, char **start, off_t off, int count, -+ int *eof, void *_dev) -+{ -+ char *buf = page; -+ struct pxa2xx_udc *dev = _dev; -+ char *next = buf; -+ unsigned size = count; -+ unsigned long flags; -+ int i, t; -+ u32 tmp; -+ -+ if (off != 0) -+ return 0; -+ -+ local_irq_save(flags); -+ -+ /* basic device status */ -+ t = snprintf(next, size, DRIVER_DESC "\n" -+ "%s version: %s\nGadget driver: %s\nHost %s\n\n", -+ driver_name, DRIVER_VERSION SIZE_STR DMASTR, -+ dev->driver ? dev->driver->driver.name : "(none)", -+ is_usb_connected() ? "full speed" : "disconnected"); -+ size -= t; -+ next += t; -+ -+ /* registers for device and ep0 */ -+ t = snprintf(next, size, -+ "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", -+ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); -+ size -= t; -+ next += t; -+ -+ tmp = UDCCR; -+ t = snprintf(next, size, -+ "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, -+ (tmp & UDCCR_REM) ? " rem" : "", -+ (tmp & UDCCR_RSTIR) ? " rstir" : "", -+ (tmp & UDCCR_SRM) ? " srm" : "", -+ (tmp & UDCCR_SUSIR) ? " susir" : "", -+ (tmp & UDCCR_RESIR) ? " resir" : "", -+ (tmp & UDCCR_RSM) ? " rsm" : "", -+ (tmp & UDCCR_UDA) ? " uda" : "", -+ (tmp & UDCCR_UDE) ? " ude" : ""); -+ size -= t; -+ next += t; -+ -+ tmp = UDCCS0; -+ t = snprintf(next, size, -+ "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, -+ (tmp & UDCCS0_SA) ? " sa" : "", -+ (tmp & UDCCS0_RNE) ? " rne" : "", -+ (tmp & UDCCS0_FST) ? " fst" : "", -+ (tmp & UDCCS0_SST) ? " sst" : "", -+ (tmp & UDCCS0_DRWF) ? " dwrf" : "", -+ (tmp & UDCCS0_FTF) ? " ftf" : "", -+ (tmp & UDCCS0_IPR) ? " ipr" : "", -+ (tmp & UDCCS0_OPR) ? " opr" : ""); -+ size -= t; -+ next += t; -+ -+ if (dev->has_cfr) { -+ tmp = UDCCFR; -+ t = snprintf(next, size, -+ "udccfr %02X =%s%s\n", tmp, -+ (tmp & UDCCFR_AREN) ? " aren" : "", -+ (tmp & UDCCFR_ACM) ? " acm" : ""); -+ size -= t; -+ next += t; -+ } -+ -+ if (!is_usb_connected() || !dev->driver) -+ goto done; -+ -+ t = snprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", -+ dev->stats.write.bytes, dev->stats.write.ops, -+ dev->stats.read.bytes, dev->stats.read.ops, -+ dev->stats.irqs); -+ size -= t; -+ next += t; -+ -+ /* dump endpoint queues */ -+ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { -+ struct pxa2xx_ep *ep = &dev->ep [i]; -+ struct pxa2xx_request *req; -+ int t; -+ -+ if (i != 0) { -+ const struct usb_endpoint_descriptor *d; -+ -+ d = ep->desc; -+ if (!d) -+ continue; -+ tmp = *dev->ep [i].reg_udccs; -+ t = snprintf(next, size, -+ "%s max %d %s udccs %02x irqs %lu/%lu\n", -+ ep->ep.name, le16_to_cpu (d->wMaxPacketSize), -+ (ep->dma >= 0) ? "dma" : "pio", tmp, -+ ep->pio_irqs, ep->dma_irqs); -+ /* TODO translate all five groups of udccs bits! */ -+ -+ } else /* ep0 should only have one transfer queued */ -+ t = snprintf(next, size, "ep0 max 16 pio irqs %lu\n", -+ ep->pio_irqs); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ -+ if (list_empty(&ep->queue)) { -+ t = snprintf(next, size, "\t(nothing queued)\n"); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ continue; -+ } -+ list_for_each_entry(req, &ep->queue, queue) { -+#ifdef USE_DMA -+ if (ep->dma >= 0 && req->queue.prev == &ep->queue) -+ t = snprintf(next, size, -+ "\treq %p len %d/%d " -+ "buf %p (dma%d dcmd %08x)\n", -+ &req->req, req->req.actual, -+ req->req.length, req->req.buf, -+ ep->dma, DCMD(ep->dma) -+ // low 13 bits == bytes-to-go -+ ); -+ else -+#endif -+ t = snprintf(next, size, -+ "\treq %p len %d/%d buf %p\n", -+ &req->req, req->req.actual, -+ req->req.length, req->req.buf); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ } -+ } -+ -+done: -+ local_irq_restore(flags); -+ *eof = 1; -+ return count - size; -+} -+ -+#define create_proc_files() \ -+ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) -+#define remove_proc_files() \ -+ remove_proc_entry(proc_node_name, NULL) -+ -+#else /* !UDC_PROC_FILE */ -+#define create_proc_files() do {} while (0) -+#define remove_proc_files() do {} while (0) -+ -+#endif /* UDC_PROC_FILE */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * udc_disable - disable USB device controller -+ */ -+static void udc_disable(struct pxa2xx_udc *dev) -+{ -+ /* block all irqs */ -+ udc_set_mask_UDCCR(UDCCR_SRM|UDCCR_REM); -+ UICR0 = UICR1 = 0xff; -+ UFNRH = UFNRH_SIM; -+ -+ /* if hardware supports it, disconnect from usb */ -+ make_usb_disappear(); -+ -+ udc_clear_mask_UDCCR(UDCCR_UDE); -+ -+#ifdef CONFIG_ARCH_PXA -+ /* Disable clock for USB device */ -+ CKEN &= ~CKEN11_USB; -+#endif -+ -+ ep0_idle (dev); -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ LED_CONNECTED_OFF; -+} -+ -+ -+/* -+ * udc_reinit - initialize software state -+ */ -+static void udc_reinit(struct pxa2xx_udc *dev) -+{ -+ u32 i; -+ -+ /* device/ep0 records init */ -+ INIT_LIST_HEAD (&dev->gadget.ep_list); -+ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); -+ dev->ep0state = EP0_IDLE; -+ -+ /* basic endpoint records init */ -+ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { -+ struct pxa2xx_ep *ep = &dev->ep[i]; -+ -+ if (i != 0) -+ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); -+ -+ ep->desc = 0; -+ ep->stopped = 0; -+ INIT_LIST_HEAD (&ep->queue); -+ ep->pio_irqs = ep->dma_irqs = 0; -+ } -+ -+ /* the rest was statically initialized, and is read-only */ -+} -+ -+/* until it's enabled, this UDC should be completely invisible -+ * to any USB host. -+ */ -+static void udc_enable (struct pxa2xx_udc *dev) -+{ -+ udc_clear_mask_UDCCR(UDCCR_UDE); -+ -+#ifdef CONFIG_ARCH_PXA -+ /* Enable clock for USB device */ -+ CKEN |= CKEN11_USB; -+#endif -+ -+ /* try to clear these bits before we enable the udc */ -+ udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); -+ -+ ep0_idle(dev); -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ dev->stats.irqs = 0; -+ -+ /* -+ * sequence taken from chapter 12.5.10, PXA250 AppProcDevManual: -+ * - enable UDC -+ * - if RESET is already in progress, ack interrupt -+ * - unmask reset interrupt -+ */ -+ udc_set_mask_UDCCR(UDCCR_UDE); -+ if (!(UDCCR & UDCCR_UDA)) -+ udc_ack_int_UDCCR(UDCCR_RSTIR); -+ -+ if (dev->has_cfr /* UDC_RES2 is defined */) { -+ /* pxa255 (a0+) can avoid a set_config race that could -+ * prevent gadget drivers from configuring correctly -+ */ -+ UDCCFR = UDCCFR_ACM; -+ } else { -+ /* "USB test mode" for pxa250 errata 40-42 (stepping a0, a1) -+ * which could result in missing packets and interrupts. -+ * supposedly one bit per endpoint, controlling whether it -+ * double buffers or not; ACM/AREN bits fit into the holes. -+ * zero bits (like USIR0_IRx) disable double buffering. -+ */ -+ UDC_RES1 = 0x00; -+ UDC_RES2 = 0x00; -+ } -+ -+#ifdef DISABLE_TEST_MODE -+ /* "test mode" seems to have become the default in later chip -+ * revs, preventing double buffering (and invalidating docs). -+ * this EXPERIMENT enables it for bulk endpoints by tweaking -+ * undefined/reserved register bits (that other drivers clear). -+ * Belcarra code comments noted this usage. -+ */ -+ if (fifo_mode & 1) { /* IN endpoints */ -+ UDC_RES1 |= USIR0_IR1|USIR0_IR6; -+ UDC_RES2 |= USIR1_IR11; -+ } -+ if (fifo_mode & 2) { /* OUT endpoints */ -+ UDC_RES1 |= USIR0_IR2|USIR0_IR7; -+ UDC_RES2 |= USIR1_IR12; -+ } -+#endif -+ -+ /* caller must be able to sleep in order to cope -+ * with startup transients. -+ */ -+ schedule_timeout(HZ/10); -+ -+ /* enable suspend/resume and reset irqs */ -+ udc_clear_mask_UDCCR(UDCCR_SRM | UDCCR_REM); -+ -+ /* enable ep0 irqs */ -+ UICR0 &= ~UICR0_IM0; -+ -+ /* if hardware supports it, connect to usb and wait for host */ -+ let_usb_appear(); -+} -+ -+ -+/* when a driver is successfully registered, it will receive -+ * control requests including set_configuration(), which enables -+ * non-control requests. then usb traffic follows until a -+ * disconnect is reported. then a host may connect again, or -+ * the driver might get unbound. -+ */ -+int usb_gadget_register_driver(struct usb_gadget_driver *driver) -+{ -+ struct pxa2xx_udc *dev = the_controller; -+ int retval; -+ -+ if (!driver -+ || driver->speed != USB_SPEED_FULL -+ || !driver->bind -+ || !driver->unbind -+ || !driver->disconnect -+ || !driver->setup) -+ return -EINVAL; -+ if (!dev) -+ return -ENODEV; -+ if (dev->driver) -+ return -EBUSY; -+ -+ /* first hook up the driver ... */ -+ dev->driver = driver; -+ -+ retval = driver->bind(&dev->gadget); -+ if (retval) { -+ DMSG("bind to driver %s --> error %d\n", -+ driver->driver.name, retval); -+ dev->driver = 0; -+ return retval; -+ } -+ -+ /* ... then enable host detection and ep0; and we're ready -+ * for set_configuration as well as eventual disconnect. -+ * NOTE: this shouldn't power up until later. -+ */ -+ DMSG("registered gadget driver '%s'\n", driver->driver.name); -+ udc_enable(dev); -+ dump_state(dev); -+ return 0; -+} -+EXPORT_SYMBOL(usb_gadget_register_driver); -+ -+static void -+stop_activity(struct pxa2xx_udc *dev, struct usb_gadget_driver *driver) -+{ -+ int i; -+ -+ /* don't disconnect drivers more than once */ -+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) -+ driver = 0; -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ -+ /* prevent new request submissions, kill any outstanding requests */ -+ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { -+ struct pxa2xx_ep *ep = &dev->ep[i]; -+ -+ ep->stopped = 1; -+ nuke(ep, -ESHUTDOWN); -+ } -+ del_timer_sync(&dev->timer); -+ -+ /* report disconnect; the driver is already quiesced */ -+ LED_CONNECTED_OFF; -+ if (driver) -+ driver->disconnect(&dev->gadget); -+ -+ /* re-init driver-visible data structures */ -+ udc_reinit(dev); -+} -+ -+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -+{ -+ struct pxa2xx_udc *dev = the_controller; -+ -+ if (!dev) -+ return -ENODEV; -+ if (!driver || driver != dev->driver) -+ return -EINVAL; -+ -+ local_irq_disable(); -+ udc_disable(dev); -+ stop_activity(dev, driver); -+ local_irq_enable(); -+ -+ driver->unbind(&dev->gadget); -+ dev->driver = 0; -+ -+ DMSG("unregistered gadget driver '%s'\n", driver->driver.name); -+ dump_state(dev); -+ return 0; -+} -+EXPORT_SYMBOL(usb_gadget_unregister_driver); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#ifdef CONFIG_ARCH_LUBBOCK -+#ifdef LUBBOCK_USB_DISC_IRQ -+ -+/* Lubbock can report connect or disconnect irqs. Likely more hardware -+ * could support it as a timer callback. -+ * -+ * FIXME for better power management, keep the hardware powered down -+ * until a host is powering the link. means scheduling work later -+ * in some task that can udc_enable(). -+ */ -+ -+#define enable_disconnect_irq() \ -+ if (machine_is_lubbock()) { enable_irq(LUBBOCK_USB_DISC_IRQ); } -+#define disable_disconnect_irq() \ -+ if (machine_is_lubbock()) { disable_irq(LUBBOCK_USB_DISC_IRQ); } -+ -+static irqreturn_t -+usb_connection_irq(int irq, void *_dev, struct pt_regs *r) -+{ -+ struct pxa2xx_udc *dev = _dev; -+ -+ dev->stats.irqs++; -+ HEX_DISPLAY(dev->stats.irqs); -+ -+ if (!is_usb_connected()) { -+ LED_CONNECTED_OFF; -+ disable_disconnect_irq(); -+ /* report disconnect just once */ -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN) { -+ DMSG("disconnect %s\n", -+ dev->driver ? dev->driver->driver.name : 0); -+ stop_activity(dev, dev->driver); -+ -+ // udc_disable (dev); -+ // no more udc irqs -+ // maybe "ACTION=disconnect /sbin/hotplug gadget". -+ } -+ } else if (dev->gadget.speed == USB_SPEED_UNKNOWN) { -+ LED_CONNECTED_ON; -+ -+ DMSG("?? connect irq ??\n"); -+ -+ // if there's no driver bound, ignore; else -+ // udc_enable (dev); -+ // UDC irqs drive the rest. -+ // maybe "ACTION=connect /sbin/hotplug gadget". -+ } -+ return IRQ_HANDLED; -+} -+ -+#endif -+#endif -+ -+#ifndef enable_disconnect_irq -+#warning USB disconnect() is not yet reported. -+#define enable_disconnect_irq() do {} while (0) -+#define disable_disconnect_irq() do {} while (0) -+#endif -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static inline void clear_ep_state (struct pxa2xx_udc *dev) -+{ -+ unsigned i; -+ -+ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint -+ * fifos, and pending transactions mustn't be continued in any case. -+ */ -+ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) -+ nuke(&dev->ep[i], -ECONNABORTED); -+} -+ -+static void udc_watchdog(unsigned long _dev) -+{ -+ struct pxa2xx_udc *dev = (void *)_dev; -+ -+ local_irq_disable(); -+ if (dev->ep0state == EP0_STALL -+ && (UDCCS0 & UDCCS0_FST) == 0 -+ && (UDCCS0 & UDCCS0_SST) == 0) { -+ UDCCS0 = UDCCS0_FST|UDCCS0_FTF; -+ DBG(DBG_VERBOSE, "ep0 re-stall\n"); -+ start_watchdog(dev); -+ } -+ local_irq_enable(); -+} -+ -+static void handle_ep0 (struct pxa2xx_udc *dev) -+{ -+ u32 udccs0 = UDCCS0; -+ struct pxa2xx_ep *ep = &dev->ep [0]; -+ struct pxa2xx_request *req; -+ union { -+ struct usb_ctrlrequest r; -+ u8 raw [8]; -+ u32 word [2]; -+ } u; -+ -+ if (list_empty(&ep->queue)) -+ req = 0; -+ else -+ req = list_entry(ep->queue.next, struct pxa2xx_request, queue); -+ -+ /* clear stall status */ -+ if (udccs0 & UDCCS0_SST) { -+ nuke(ep, -EPIPE); -+ UDCCS0 = UDCCS0_SST; -+ del_timer(&dev->timer); -+ ep0_idle(dev); -+ } -+ -+ /* previous request unfinished? non-error iff back-to-back ... */ -+ if ((udccs0 & UDCCS0_SA) != 0 && dev->ep0state != EP0_IDLE) { -+ nuke(ep, 0); -+ del_timer(&dev->timer); -+ ep0_idle(dev); -+ } -+ -+ switch (dev->ep0state) { -+ case EP0_IDLE: -+ /* late-breaking status? */ -+ udccs0 = UDCCS0; -+ -+ /* start control request? */ -+ if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE)) -+ == (UDCCS0_OPR|UDCCS0_SA|UDCCS0_RNE))) { -+ int i; -+ -+ nuke (ep, -EPROTO); -+ -+ /* read SETUP packet */ -+ for (i = 0; i < 8; i++) { -+ if (unlikely(!(UDCCS0 & UDCCS0_RNE))) { -+bad_setup: -+ DMSG("SETUP %d!\n", i); -+ goto stall; -+ } -+ u.raw [i] = (u8) UDDR0; -+ } -+ if (unlikely((UDCCS0 & UDCCS0_RNE) != 0)) -+ goto bad_setup; -+ -+got_setup: -+ le16_to_cpus (&u.r.wValue); -+ le16_to_cpus (&u.r.wIndex); -+ le16_to_cpus (&u.r.wLength); -+ -+ LED_EP0_ON; -+ DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", -+ u.r.bRequestType, u.r.bRequest, -+ u.r.wValue, u.r.wIndex, u.r.wLength); -+ -+ /* cope with automagic for some standard requests. */ -+ dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) -+ == USB_TYPE_STANDARD; -+ dev->req_config = 0; -+ dev->req_pending = 1; -+ switch (u.r.bRequest) { -+ /* hardware restricts gadget drivers here! */ -+ case USB_REQ_SET_CONFIGURATION: -+ if (u.r.bRequestType == USB_RECIP_DEVICE) { -+ /* reflect hardware's automagic -+ * up to the gadget driver. -+ */ -+config_change: -+ dev->req_config = 1; -+ clear_ep_state(dev); -+ /* if !has_cfr, there's no synch -+ * else use AREN (later) not SA|OPR -+ * USIR0_IR0 acts edge sensitive -+ */ -+ } -+ break; -+ /* ... and here, even more ... */ -+ case USB_REQ_SET_INTERFACE: -+ if (u.r.bRequestType == USB_RECIP_INTERFACE) { -+ /* udc hardware is broken by design: -+ * - altsetting may only be zero; -+ * - hw resets all interfaces' eps; -+ * - ep reset doesn't include halt(?). -+ */ -+ DMSG("broken set_interface (%d/%d)\n", -+ u.r.wIndex, u.r.wValue); -+ goto config_change; -+ } -+ break; -+ /* hardware was supposed to hide this */ -+ case USB_REQ_SET_ADDRESS: -+ if (u.r.bRequestType == USB_RECIP_DEVICE) { -+ ep0start(dev, 0, "address"); -+ return; -+ } -+ break; -+ } -+ -+ if (u.r.bRequestType & USB_DIR_IN) -+ dev->ep0state = EP0_IN_DATA_PHASE; -+ else -+ dev->ep0state = EP0_OUT_DATA_PHASE; -+ -+ i = dev->driver->setup(&dev->gadget, &u.r); -+ if (i < 0) { -+ /* hardware automagic preventing STALL... */ -+ if (dev->req_config) { -+ /* hardware sometimes neglects to tell -+ * tell us about config change events, -+ * so later ones may fail... -+ */ -+ WARN("config change %02x fail %d?\n", -+ u.r.bRequest, i); -+ return; -+ /* TODO experiment: if has_cfr, -+ * hardware didn't ACK; maybe we -+ * could actually STALL! -+ */ -+ } -+ DBG(DBG_VERBOSE, "protocol STALL, " -+ "%02x err %d\n", UDCCS0, i); -+stall: -+ /* the watchdog timer helps deal with cases -+ * where udc seems to clear FST wrongly, and -+ * then NAKs instead of STALLing. -+ */ -+ ep0start(dev, UDCCS0_FST|UDCCS0_FTF, "stall"); -+ start_watchdog(dev); -+ dev->ep0state = EP0_STALL; -+ LED_EP0_OFF; -+ -+ /* deferred i/o == no response yet */ -+ } else if (dev->req_pending) { -+ if (likely(dev->ep0state == EP0_IN_DATA_PHASE -+ || dev->req_std || u.r.wLength)) -+ ep0start(dev, 0, "defer"); -+ else -+ ep0start(dev, UDCCS0_IPR, "defer/IPR"); -+ } -+ -+ /* expect at least one data or status stage irq */ -+ return; -+ -+ } else if (likely((udccs0 & (UDCCS0_OPR|UDCCS0_SA)) -+ == (UDCCS0_OPR|UDCCS0_SA))) { -+ unsigned i; -+ -+ /* pxa210/250 erratum 131 for B0/B1 says RNE lies. -+ * still observed on a pxa255 a0. -+ */ -+ DBG(DBG_VERBOSE, "e131\n"); -+ nuke(ep, -EPROTO); -+ -+ /* read SETUP data, but don't trust it too much */ -+ for (i = 0; i < 8; i++) -+ u.raw [i] = (u8) UDDR0; -+ if ((u.r.bRequestType & USB_RECIP_MASK) -+ > USB_RECIP_OTHER) -+ goto stall; -+ if (u.word [0] == 0 && u.word [1] == 0) -+ goto stall; -+ goto got_setup; -+ } else { -+ /* some random early IRQ: -+ * - we acked FST -+ * - IPR cleared -+ * - OPR got set, without SA (likely status stage) -+ */ -+ UDCCS0 = udccs0 & (UDCCS0_SA|UDCCS0_OPR); -+ } -+ break; -+ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ -+ if (udccs0 & UDCCS0_OPR) { -+ UDCCS0 = UDCCS0_OPR|UDCCS0_FTF; -+ DBG(DBG_VERBOSE, "ep0in premature status\n"); -+ if (req) -+ done(ep, req, 0); -+ ep0_idle(dev); -+ } else /* irq was IPR clearing */ { -+ if (req) { -+ /* this IN packet might finish the request */ -+ (void) write_ep0_fifo(ep, req); -+ } /* else IN token before response was written */ -+ } -+ break; -+ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ -+ if (udccs0 & UDCCS0_OPR) { -+ if (req) { -+ /* this OUT packet might finish the request */ -+ if (read_ep0_fifo(ep, req)) -+ done(ep, req, 0); -+ /* else more OUT packets expected */ -+ } /* else OUT token before read was issued */ -+ } else /* irq was IPR clearing */ { -+ DBG(DBG_VERBOSE, "ep0out premature status\n"); -+ if (req) -+ done(ep, req, 0); -+ ep0_idle(dev); -+ } -+ break; -+ case EP0_END_XFER: -+ if (req) -+ done(ep, req, 0); -+ /* ack control-IN status (maybe in-zlp was skipped) -+ * also appears after some config change events. -+ */ -+ if (udccs0 & UDCCS0_OPR) -+ UDCCS0 = UDCCS0_OPR; -+ ep0_idle(dev); -+ break; -+ case EP0_STALL: -+ UDCCS0 = UDCCS0_FST; -+ break; -+ } -+ USIR0 = USIR0_IR0; -+} -+ -+static void handle_ep(struct pxa2xx_ep *ep) -+{ -+ struct pxa2xx_request *req; -+ int is_in = ep->bEndpointAddress & USB_DIR_IN; -+ int completed; -+ u32 udccs, tmp; -+ -+ do { -+ completed = 0; -+ if (likely (!list_empty(&ep->queue))) -+ req = list_entry(ep->queue.next, -+ struct pxa2xx_request, queue); -+ else -+ req = 0; -+ -+ // TODO check FST handling -+ -+ udccs = *ep->reg_udccs; -+ if (unlikely(is_in)) { /* irq from TPC, SST, or (ISO) TUR */ -+ tmp = UDCCS_BI_TUR; -+ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) -+ tmp |= UDCCS_BI_SST; -+ tmp &= udccs; -+ if (likely (tmp)) -+ *ep->reg_udccs = tmp; -+ if (req && likely ((udccs & UDCCS_BI_TFS) != 0)) -+ completed = write_fifo(ep, req); -+ -+ } else { /* irq from RPC (or for ISO, ROF) */ -+ if (likely(ep->bmAttributes == USB_ENDPOINT_XFER_BULK)) -+ tmp = UDCCS_BO_SST | UDCCS_BO_DME; -+ else -+ tmp = UDCCS_IO_ROF | UDCCS_IO_DME; -+ tmp &= udccs; -+ if (likely(tmp)) -+ *ep->reg_udccs = tmp; -+ -+ /* fifos can hold packets, ready for reading... */ -+ if (likely(req != 0)) { -+#ifdef USE_OUT_DMA -+// TODO didn't yet debug out-dma. this approach assumes -+// the worst about short packets and RPC; it might be better. -+ -+ if (likely(ep->dma >= 0)) { -+ if (!(udccs & UDCCS_BO_RSP)) { -+ *ep->reg_udccs = UDCCS_BO_RPC; -+ ep->dma_irqs++; -+ return; -+ } -+ } -+#endif -+ completed = read_fifo(ep, req); -+ } else -+ pio_irq_disable (ep->bEndpointAddress); -+ } -+ ep->pio_irqs++; -+ } while (completed); -+} -+ -+/* -+ * pxa2xx_udc_irq - interrupt handler -+ * -+ * avoid delays in ep0 processing. the control handshaking isn't always -+ * under software control (pxa250c0 and the pxa255 are better), and delays -+ * could cause usb protocol errors. -+ */ -+static irqreturn_t -+pxa2xx_udc_irq(int irq, void *_dev, struct pt_regs *r) -+{ -+ struct pxa2xx_udc *dev = _dev; -+ int handled; -+ -+ dev->stats.irqs++; -+ HEX_DISPLAY(dev->stats.irqs); -+ do { -+ u32 udccr = UDCCR; -+ -+ handled = 0; -+ -+ /* SUSpend Interrupt Request */ -+ if (unlikely(udccr & UDCCR_SUSIR)) { -+ udc_ack_int_UDCCR(UDCCR_SUSIR); -+ handled = 1; -+ DBG(DBG_VERBOSE, "USB suspend%s\n", is_usb_connected() -+ ? "" : "+disconnect"); -+ -+ if (!is_usb_connected()) -+ stop_activity(dev, dev->driver); -+ else if (dev->gadget.speed != USB_SPEED_UNKNOWN -+ && dev->driver -+ && dev->driver->suspend) -+ dev->driver->suspend(&dev->gadget); -+ ep0_idle (dev); -+ } -+ -+ /* RESume Interrupt Request */ -+ if (unlikely(udccr & UDCCR_RESIR)) { -+ udc_ack_int_UDCCR(UDCCR_RESIR); -+ handled = 1; -+ DBG(DBG_VERBOSE, "USB resume\n"); -+ -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN -+ && dev->driver -+ && dev->driver->resume -+ && is_usb_connected()) -+ dev->driver->resume(&dev->gadget); -+ } -+ -+ /* ReSeT Interrupt Request - USB reset */ -+ if (unlikely(udccr & UDCCR_RSTIR)) { -+ udc_ack_int_UDCCR(UDCCR_RSTIR); -+ handled = 1; -+ -+ if ((UDCCR & UDCCR_UDA) == 0) { -+ DBG(DBG_VERBOSE, "USB reset start\n"); -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN) -+ disable_disconnect_irq(); -+ -+ /* reset driver and endpoints, -+ * in case that's not yet done -+ */ -+ stop_activity (dev, dev->driver); -+ -+ } else { -+ INFO("USB reset\n"); -+ dev->gadget.speed = USB_SPEED_FULL; -+ LED_CONNECTED_ON; -+ memset(&dev->stats, 0, sizeof dev->stats); -+ /* driver and endpoints are still reset */ -+ enable_disconnect_irq(); -+ } -+ -+ } else { -+ u32 usir0 = USIR0 & ~UICR0; -+ u32 usir1 = USIR1 & ~UICR1; -+ int i; -+ -+ if (unlikely (!usir0 && !usir1)) -+ continue; -+ -+ DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", usir1, usir0); -+ -+ /* control traffic */ -+ if (usir0 & USIR0_IR0) { -+ dev->ep[0].pio_irqs++; -+ handle_ep0(dev); -+ handled = 1; -+ } -+ -+ /* endpoint data transfers */ -+ for (i = 0; i < 8; i++) { -+ u32 tmp = 1 << i; -+ -+ if (i && (usir0 & tmp)) { -+ handle_ep(&dev->ep[i]); -+ USIR0 |= tmp; -+ handled = 1; -+ } -+ if (usir1 & tmp) { -+ handle_ep(&dev->ep[i+8]); -+ USIR1 |= tmp; -+ handled = 1; -+ } -+ } -+ } -+ -+ /* we could also ask for 1 msec SOF (SIR) interrupts */ -+ -+ } while (handled); -+ return IRQ_HANDLED; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * cleanup - free resources allocated during init -+ */ -+static void /*__exit and */ __init cleanup(void) -+{ -+ struct pxa2xx_udc *dev = the_controller; -+ -+ if (!dev) -+ return; -+ -+ udc_disable(dev); -+ remove_proc_files(); -+ usb_gadget_unregister_driver(dev->driver); -+ if (dev->got_irq) { -+ free_irq(IRQ_USB, dev); -+ dev->got_irq = 0; -+ } -+#ifdef LUBBOCK_USB_DISC_IRQ -+ if (dev->got_disc) { -+ free_irq(LUBBOCK_USB_DISC_IRQ, dev); -+ dev->got_disc = 0; -+ } -+#endif -+ the_controller = 0; -+ release_mem_region(REGISTER_FIRST, REGISTER_LENGTH); -+} -+module_exit (cleanup); -+ -+/* this uses load-time allocation and initialization (instead of -+ * doing it at run-time) to save code, eliminate fault paths, and -+ * be more obviously correct. -+ */ -+static struct pxa2xx_udc memory = { -+ .gadget = { -+ .ops = &pxa2xx_udc_ops, -+ .ep0 = &memory.ep[0].ep, -+ .name = driver_name, -+ .dev = { -+ .bus_id = "gadget", -+ }, -+ }, -+ -+ /* control endpoint */ -+ .ep[0] = { -+ .ep = { -+ .name = ep0name, -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = EP0_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .reg_udccs = &UDCCS0, -+ .reg_uddr = &UDDR0, -+ }, -+ -+ /* first group of endpoints */ -+ .ep[1] = { -+ .ep = { -+ .name = "ep1in-bulk", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = BULK_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = BULK_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 1, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .reg_udccs = &UDCCS1, -+ .reg_uddr = &UDDR1, -+ drcmr (25) -+ }, -+ .ep[2] = { -+ .ep = { -+ .name = "ep2out-bulk", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = BULK_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = BULK_FIFO_SIZE, -+ .bEndpointAddress = 2, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .reg_udccs = &UDCCS2, -+ .reg_ubcr = &UBCR2, -+ .reg_uddr = &UDDR2, -+ drcmr (26) -+ }, -+#ifndef CONFIG_USB_PXA2XX_SMALL -+ .ep[3] = { -+ .ep = { -+ .name = "ep3in-iso", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = ISO_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = ISO_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 3, -+ .bmAttributes = USB_ENDPOINT_XFER_ISOC, -+ .reg_udccs = &UDCCS3, -+ .reg_uddr = &UDDR3, -+ drcmr (27) -+ }, -+ .ep[4] = { -+ .ep = { -+ .name = "ep4out-iso", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = ISO_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = ISO_FIFO_SIZE, -+ .bEndpointAddress = 4, -+ .bmAttributes = USB_ENDPOINT_XFER_ISOC, -+ .reg_udccs = &UDCCS4, -+ .reg_ubcr = &UBCR4, -+ .reg_uddr = &UDDR4, -+ drcmr (28) -+ }, -+ .ep[5] = { -+ .ep = { -+ .name = "ep5in-int", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = INT_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = INT_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 5, -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .reg_udccs = &UDCCS5, -+ .reg_uddr = &UDDR5, -+ }, -+ -+ /* second group of endpoints */ -+ .ep[6] = { -+ .ep = { -+ .name = "ep6in-bulk", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = BULK_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = BULK_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 6, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .reg_udccs = &UDCCS6, -+ .reg_uddr = &UDDR6, -+ drcmr (30) -+ }, -+ .ep[7] = { -+ .ep = { -+ .name = "ep7out-bulk", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = BULK_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = BULK_FIFO_SIZE, -+ .bEndpointAddress = 7, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .reg_udccs = &UDCCS7, -+ .reg_ubcr = &UBCR7, -+ .reg_uddr = &UDDR7, -+ drcmr (31) -+ }, -+ .ep[8] = { -+ .ep = { -+ .name = "ep8in-iso", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = ISO_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = ISO_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 8, -+ .bmAttributes = USB_ENDPOINT_XFER_ISOC, -+ .reg_udccs = &UDCCS8, -+ .reg_uddr = &UDDR8, -+ drcmr (32) -+ }, -+ .ep[9] = { -+ .ep = { -+ .name = "ep9out-iso", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = ISO_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = ISO_FIFO_SIZE, -+ .bEndpointAddress = 9, -+ .bmAttributes = USB_ENDPOINT_XFER_ISOC, -+ .reg_udccs = &UDCCS9, -+ .reg_ubcr = &UBCR9, -+ .reg_uddr = &UDDR9, -+ drcmr (33) -+ }, -+ .ep[10] = { -+ .ep = { -+ .name = "ep10in-int", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = INT_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = INT_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 10, -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .reg_udccs = &UDCCS10, -+ .reg_uddr = &UDDR10, -+ }, -+ -+ /* third group of endpoints */ -+ .ep[11] = { -+ .ep = { -+ .name = "ep11in-bulk", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = BULK_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = BULK_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 11, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .reg_udccs = &UDCCS11, -+ .reg_uddr = &UDDR11, -+ drcmr (35) -+ }, -+ .ep[12] = { -+ .ep = { -+ .name = "ep12out-bulk", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = BULK_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = BULK_FIFO_SIZE, -+ .bEndpointAddress = 12, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .reg_udccs = &UDCCS12, -+ .reg_ubcr = &UBCR12, -+ .reg_uddr = &UDDR12, -+ drcmr (36) -+ }, -+ .ep[13] = { -+ .ep = { -+ .name = "ep13in-iso", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = ISO_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = ISO_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 13, -+ .bmAttributes = USB_ENDPOINT_XFER_ISOC, -+ .reg_udccs = &UDCCS13, -+ .reg_uddr = &UDDR13, -+ drcmr (37) -+ }, -+ .ep[14] = { -+ .ep = { -+ .name = "ep14out-iso", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = ISO_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = ISO_FIFO_SIZE, -+ .bEndpointAddress = 14, -+ .bmAttributes = USB_ENDPOINT_XFER_ISOC, -+ .reg_udccs = &UDCCS14, -+ .reg_ubcr = &UBCR14, -+ .reg_uddr = &UDDR14, -+ drcmr (38) -+ }, -+ .ep[15] = { -+ .ep = { -+ .name = "ep15in-int", -+ .ops = &pxa2xx_ep_ops, -+ .maxpacket = INT_FIFO_SIZE, -+ }, -+ .dev = &memory, -+ .fifo_size = INT_FIFO_SIZE, -+ .bEndpointAddress = USB_DIR_IN | 15, -+ .bmAttributes = USB_ENDPOINT_XFER_INT, -+ .reg_udccs = &UDCCS15, -+ .reg_uddr = &UDDR15, -+ }, -+#endif /* !CONFIG_USB_PXA2XX_SMALL */ -+}; -+ -+#define CP15R0_VENDOR_MASK 0xffffe000 -+ -+#if defined(CONFIG_ARCH_PXA) -+#define CP15R0_XSCALE_VALUE 0x69052000 /* intel/arm/xscale */ -+ -+#elif defined(CONFIG_ARCH_IXP425) -+#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/ixp425 */ -+ -+#endif -+ -+#define CP15R0_PROD_MASK 0x000003f0 -+#define PXA25x 0x00000100 /* and PXA26x */ -+#define PXA210 0x00000120 -+ -+#define CP15R0_REV_MASK 0x0000000f -+ -+#define CP15R0_PRODREV_MASK (CP15R0_PROD_MASK | CP15R0_REV_MASK) -+ -+#define PXA255_A0 0x00000106 /* or PXA260_B1 */ -+#define PXA250_C0 0x00000105 /* or PXA26x_B0 */ -+#define PXA250_B2 0x00000104 -+#define PXA250_B1 0x00000103 /* or PXA260_A0 */ -+#define PXA250_B0 0x00000102 -+#define PXA250_A1 0x00000101 -+#define PXA250_A0 0x00000100 -+ -+#define PXA210_C0 0x00000125 -+#define PXA210_B2 0x00000124 -+#define PXA210_B1 0x00000123 -+#define PXA210_B0 0x00000122 -+ -+#define IXP425_A0 0x000001c1 -+ -+/* -+ * init - allocate resources -+ */ -+static int __init init(void) -+{ -+ struct pxa2xx_udc *dev; -+ int retval, out_dma = 1; -+ u32 chiprev; -+ -+ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); -+ -+ /* insist on Intel/ARM/XScale */ -+ asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); -+ if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { -+ printk(KERN_ERR "%s: not XScale!\n", driver_name); -+ return -ENODEV; -+ } -+ -+ /* allocate resources */ -+ if (!request_mem_region(REGISTER_FIRST, REGISTER_LENGTH, driver_name)) -+ return -EBUSY; -+ -+ /* initialize data */ -+ dev = &memory; -+ -+ init_timer(&dev->timer); -+ dev->timer.function = udc_watchdog; -+ dev->timer.data = (unsigned long) dev; -+ -+ /* trigger chiprev-specific logic */ -+ switch (chiprev & CP15R0_PRODREV_MASK) { -+#if defined(CONFIG_ARCH_PXA) -+ case PXA255_A0: -+ dev->has_cfr = 1; -+ break; -+ case PXA250_A0: -+ case PXA250_A1: -+ /* A0/A1 "not released"; ep 13, 15 unusable */ -+ /* fall through */ -+ case PXA250_B2: case PXA210_B2: -+ case PXA250_B1: case PXA210_B1: -+ case PXA250_B0: case PXA210_B0: -+ out_dma = 0; -+ /* fall through */ -+ case PXA250_C0: case PXA210_C0: -+ break; -+#elif defined(CONFIG_ARCH_IXP425) -+ case IXP425_A0: -+ out_dma = 0; -+ break; -+#endif -+ default: -+ out_dma = 0; -+ printk(KERN_ERR "%s: unrecognized processor: %08x\n", -+ driver_name, chiprev); -+ return -ENODEV; -+ } -+ -+ pr_debug("%s: IRQ %d%s%s%s\n", driver_name, IRQ_USB, -+ dev->has_cfr ? "" : " (!cfr)", -+ out_dma ? "" : " (broken dma-out)", -+ SIZE_STR DMASTR -+ ); -+ -+#ifdef USE_DMA -+#ifndef USE_OUT_DMA -+ out_dma = 0; -+#endif -+ /* pxa 250 erratum 130 prevents using OUT dma (fixed C0) */ -+ if (!out_dma) { -+ DMSG("disabled OUT dma\n"); -+ dev->ep[ 2].reg_drcmr = dev->ep[ 4].reg_drcmr = 0; -+ dev->ep[ 7].reg_drcmr = dev->ep[ 9].reg_drcmr = 0; -+ dev->ep[12].reg_drcmr = dev->ep[14].reg_drcmr = 0; -+ } -+#endif -+ -+ the_controller = dev; -+ udc_disable(dev); -+ udc_reinit(dev); -+ -+ /* irq setup after old hardware state is cleaned up */ -+ retval = request_irq(IRQ_USB, pxa2xx_udc_irq, -+ SA_INTERRUPT, driver_name, dev); -+ if (retval != 0) { -+ printk(KERN_ERR "%s: can't get irq %i, err %d\n", -+ driver_name, IRQ_USB, retval); -+ return -EBUSY; -+ } -+ dev->got_irq = 1; -+ -+#ifdef LUBBOCK_USB_DISC_IRQ -+ if (machine_is_lubbock()) { -+ disable_irq(LUBBOCK_USB_DISC_IRQ); -+ retval = request_irq(LUBBOCK_USB_DISC_IRQ, -+ usb_connection_irq, -+ SA_INTERRUPT | SA_SAMPLE_RANDOM, -+ driver_name, dev); -+ if (retval != 0) { -+ enable_irq(LUBBOCK_USB_DISC_IRQ); -+ printk(KERN_ERR "%s: can't get irq %i, err %d\n", -+ driver_name, LUBBOCK_USB_DISC_IRQ, retval); -+ cleanup(); -+ return retval; -+ } -+ dev->got_disc = 1; -+ } -+#endif -+ -+ create_proc_files(); -+ return 0; -+} -+module_init (init); -+ -+MODULE_DESCRIPTION(DRIVER_DESC); -+MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); -+MODULE_LICENSE("GPL"); -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/pxa2xx_udc.h kernel/drivers/usb/gadget/pxa2xx_udc.h ---- /tmp/kernel/drivers/usb/gadget/pxa2xx_udc.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/pxa2xx_udc.h 2005-04-22 17:53:19.496529422 +0200 -@@ -0,0 +1,528 @@ -+/* -+ * linux/drivers/usb/gadget/pxa2xx_udc.h -+ * Intel PXA2xx on-chip full speed USB device controller -+ * -+ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix -+ * Copyright (C) 2003 David Brownell -+ * Copyright (C) 2003 Joshua Wise -+ * -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __LINUX_USB_GADGET_PXA2XX_H -+#define __LINUX_USB_GADGET_PXA2XX_H -+ -+#include <linux/types.h> -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* pxa2xx has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ -+#define UFNRH_SIR (1 << 7) /* SOF interrupt request */ -+#define UFNRH_SIM (1 << 6) /* SOF interrupt mask */ -+#define UFNRH_IPE14 (1 << 5) /* ISO packet error, ep14 */ -+#define UFNRH_IPE9 (1 << 4) /* ISO packet error, ep9 */ -+#define UFNRH_IPE4 (1 << 3) /* ISO packet error, ep4 */ -+ -+/* pxa255 has this (move to include/asm-arm/arch-pxa/pxa-regs.h) */ -+#define UDCCFR UDC_RES2 /* UDC Control Function Register */ -+#define UDCCFR_AREN (1 << 7) /* ACK response enable (now) */ -+#define UDCCFR_ACM (1 << 2) /* ACK control mode (wait for AREN) */ -+ -+/* for address space reservation */ -+#define REGISTER_FIRST ((unsigned long)(&UDCCR)) -+#define REGISTER_LAST ((unsigned long)(&UDDR14)) /* not UDDR15! */ -+#define REGISTER_LENGTH ((REGISTER_LAST - REGISTER_FIRST) + 4) -+ -+/*-------------------------------------------------------------------------*/ -+ -+struct pxa2xx_udc; -+ -+struct pxa2xx_ep { -+ struct usb_ep ep; -+ struct pxa2xx_udc *dev; -+ -+ const struct usb_endpoint_descriptor *desc; -+ struct list_head queue; -+ unsigned long pio_irqs; -+ unsigned long dma_irqs; -+ short dma; -+ -+ unsigned short fifo_size; -+ u8 bEndpointAddress; -+ u8 bmAttributes; -+ -+ unsigned stopped : 1; -+ unsigned dma_fixup : 1; -+ -+ /* UDCCS = UDC Control/Status for this EP -+ * UBCR = UDC Byte Count Remaining (contents of OUT fifo) -+ * UDDR = UDC Endpoint Data Register (the fifo) -+ * DRCM = DMA Request Channel Map -+ */ -+ volatile u32 *reg_udccs; -+ volatile u32 *reg_ubcr; -+ volatile u32 *reg_uddr; -+#ifdef USE_DMA -+ volatile u32 *reg_drcmr; -+#define drcmr(n) .reg_drcmr = & DRCMR ## n , -+#else -+#define drcmr(n) -+#endif -+}; -+ -+struct pxa2xx_request { -+ struct usb_request req; -+ struct list_head queue; -+}; -+ -+enum ep0_state { -+ EP0_IDLE, -+ EP0_IN_DATA_PHASE, -+ EP0_OUT_DATA_PHASE, -+ EP0_END_XFER, -+ EP0_STALL, -+}; -+ -+#define EP0_FIFO_SIZE ((unsigned)16) -+#define BULK_FIFO_SIZE ((unsigned)64) -+#define ISO_FIFO_SIZE ((unsigned)256) -+#define INT_FIFO_SIZE ((unsigned)8) -+ -+struct udc_stats { -+ struct ep0stats { -+ unsigned long ops; -+ unsigned long bytes; -+ } read, write; -+ unsigned long irqs; -+}; -+ -+#ifdef CONFIG_USB_PXA2XX_SMALL -+/* when memory's tight, SMALL config saves code+data. */ -+#undef USE_DMA -+#define PXA_UDC_NUM_ENDPOINTS 3 -+#endif -+ -+#ifndef PXA_UDC_NUM_ENDPOINTS -+#define PXA_UDC_NUM_ENDPOINTS 16 -+#endif -+ -+struct pxa2xx_udc { -+ struct usb_gadget gadget; -+ struct usb_gadget_driver *driver; -+ -+ enum ep0_state ep0state; -+ struct udc_stats stats; -+ unsigned got_irq : 1, -+ got_disc : 1, -+ has_cfr : 1, -+ req_pending : 1, -+ req_std : 1, -+ req_config : 1; -+ -+#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) -+ struct timer_list timer; -+ -+ struct pxa2xx_ep ep [PXA_UDC_NUM_ENDPOINTS]; -+}; -+ -+/* 2.5 changes ... */ -+ -+#ifndef container_of -+#define container_of list_entry -+#endif -+ -+#ifndef WARN_ON -+#define WARN_ON BUG_ON -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* please keep machine-specific defines in alphabetical order. */ -+ -+// CONFIG_ARCH_ADI_COYOTE behaves -+ -+#ifdef CONFIG_ARCH_E7XX -+# include <asm/arch/e7xx-gpio.h> -+#endif -+ -+#ifdef CONFIG_ARCH_H1900 -+# include <asm/arch/h1900-gpio.h> -+#endif -+ -+#ifdef CONFIG_ARCH_H3900 -+# include <asm/arch/h3900-gpio.h> -+#endif -+ -+#ifdef CONFIG_ARCH_H5400 -+# include <asm/arch/h5400-gpio.h> -+#endif -+ -+#ifdef CONFIG_ARCH_INNOKOM -+#include <asm/arch/innokom.h> -+#endif -+ -+#ifdef CONFIG_ARCH_LUBBOCK -+#include <asm/arch/lubbock.h> -+/* lubbock can also report usb connect/disconnect irqs */ -+ -+#ifdef DEBUG -+#define HEX_DISPLAY(n) if (machine_is_lubbock()) { LUB_HEXLED = (n); } -+ -+#define LED_CONNECTED_ON if (machine_is_lubbock()) { \ -+ DISCRETE_LED_ON(D26); } -+#define LED_CONNECTED_OFF if(machine_is_lubbock()) { \ -+ DISCRETE_LED_OFF(D26); LUB_HEXLED = 0; } -+#define LED_EP0_ON if (machine_is_lubbock()) { DISCRETE_LED_ON(D25); } -+#define LED_EP0_OFF if (machine_is_lubbock()) { DISCRETE_LED_OFF(D25); } -+#endif /* DEBUG */ -+ -+#endif -+ -+#ifdef CONFIG_ARCH_PXA_CORGI -+/* Sharp Zaurus C-700, C-750, C-760, C-860 */ -+#define CORGI_CONNECT_GPIO 45 -+/* use the ARM-Linux registered symbol, not a Lineo-private one */ -+#define CONFIG_MACH_CORGI -+#endif -+ -+#ifdef CONFIG_ARCH_PXA_POODLE -+/* Sharp B-500, SL-5600 */ -+#define POODLE_CONNECT_GPIO 20 -+/* use the ARM-Linux registered symbol, not a Lineo-private one */ -+#define CONFIG_MACH_POODLE -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* LEDs are only for debug */ -+#ifndef HEX_DISPLAY -+#define HEX_DISPLAY(n) do {} while(0) -+#endif -+ -+#ifndef LED_CONNECTED_ON -+#define LED_CONNECTED_ON do {} while(0) -+#define LED_CONNECTED_OFF do {} while(0) -+#endif -+#ifndef LED_EP0_ON -+#define LED_EP0_ON do {} while (0) -+#define LED_EP0_OFF do {} while (0) -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct pxa2xx_udc *the_controller; -+ -+/* one GPIO should be used to detect host disconnect */ -+static int is_usb_connected(void) -+{ -+ static int first = 0; -+ -+ // CONFIG_ARCH_ADI_COYOTE cannot detect or force disconnect -+#ifdef CONFIG_ARCH_E7XX -+ if (machine_is_e7xx()) -+ return (GPLR(GPIO_E7XX_USB_DISC) -+ & GPIO_bit(GPIO_E7XX_USB_DISC)); -+#endif -+#if 0 -+#ifdef CONFIG_ARCH_H1900 -+ if (machine_is_h1900()) -+ return (!(GPLR(GPIO_NR_H1900_USB_DETECT_N) -+ & GPIO_bit(GPIO_NR_H1900_USB_DETECT_N))); -+#endif -+#ifdef CONFIG_ARCH_H3900 -+ if (machine_is_h3900()) -+ return 1; -+#endif -+#ifdef CONFIG_ARCH_H5400 -+ // h5400 ... ? -+#endif -+#endif -+#ifdef CONFIG_ARCH_INNOKOM -+ if (machine_is_innokom()) -+ return (GPLR(GPIO_INNOKOM_USB_DISC) -+ & GPIO_bit(GPIO_INNOKOM_USB_DISC)); -+#endif -+#ifdef CONFIG_ARCH_LUBBOCK -+ if (machine_is_lubbock()) -+ return ((LUB_MISC_RD & (1 << 9)) == 0); -+#endif -+ // Sharp's sources didn't show a corgi or poodle hook -+ -+ if (!first) { -+ pr_info("%s: can't check host connect\n", driver_name); -+ first++; -+ } -+ return 1; -+} -+ -+static int disc_first = 0; -+ -+/* one GPIO should force the host to see this device (or not) */ -+static void make_usb_disappear(void) -+{ -+ // CONFIG_ARCH_ADI_COYOTE cannot detect or force disconnect -+#ifdef CONFIG_ARCH_E7XX -+ if (machine_is_e7xx()) { -+ GPSR(GPIO_E7XX_USB_PULLUP) = GPIO_bit(GPIO_E7XX_USB_PULLUP); -+ return; -+ } -+#endif -+ // h1900 ... ? -+#ifdef CONFIG_ARCH_H3900 -+ if (machine_is_h3900()) { -+ GPDR0 &= ~GPIO_H3900_USBP_PULLUP; -+ return; -+ } -+#endif -+#ifdef CONFIG_ARCH_H5400 -+ if (machine_is_h5400()) { -+ GPDR(GPIO_NR_H5400_USB_PULLUP) &= -+ ~GPIO_bit(GPIO_NR_H5400_USB_PULLUP); -+ return; -+ } -+#endif -+#ifdef CONFIG_ARCH_INNOKOM -+ if (machine_is_innokom()) { -+ GPSR(GPIO_INNOKOM_USB_ONOFF) = GPIO_bit(GPIO_INNOKOM_USB_ONOFF); -+ printk("innokom: disappear\n"); -+ udelay(5); -+ return; -+ } -+#endif -+#ifdef CONFIG_ARCH_CSB226 -+ if (machine_is_csb226()) { -+ GPCR0 |= 0x00000080; -+ printk("csb226: disappear\n"); -+ udelay(5); -+ return; -+ } -+#endif -+ // lubbock has no D+ pullup -+#ifdef CONFIG_MACH_CORGI -+ if (machine_is_corgi()) { -+ GPDR(CORGI_CONNECT_GPIO) |= GPIO_bit(CORGI_CONNECT_GPIO); -+ GPCR(CORGI_CONNECT_GPIO) = GPIO_bit(CORGI_CONNECT_GPIO); -+ } -+#endif -+#ifdef CONFIG_MACH_POODLE -+ if (machine_is_poodle()) { -+ GPDR(POODLE_CONNECT_GPIO) |= GPIO_bit(POODLE_CONNECT_GPIO); -+ GPCR(POODLE_CONNECT_GPIO) = GPIO_bit(POODLE_CONNECT_GPIO); -+ } -+#endif -+ -+ if (!disc_first) { -+ pr_info("%s: can't force usb disconnect\n", driver_name); -+ disc_first++; -+ } -+} -+ -+static void let_usb_appear(void) -+{ -+ // CONFIG_ARCH_ADI_COYOTE cannot detect or force disconnect -+#ifdef CONFIG_ARCH_E7XX -+ if (machine_is_e7xx()) { -+ GPCR(GPIO_E7XX_USB_PULLUP) = GPIO_bit(GPIO_E7XX_USB_PULLUP); -+ return; -+ } -+#endif -+ // h1900 ... ? -+#ifdef CONFIG_ARCH_H3900 -+ if (machine_is_h3900()) { -+ GPDR0 |= GPIO_H3900_USBP_PULLUP; -+ GPSR0 |= GPIO_H3900_USBP_PULLUP; -+ return; -+ } -+#endif -+#ifdef CONFIG_ARCH_H5400 -+ if (machine_is_h5400()) { -+ GPDR(GPIO_NR_H5400_USB_PULLUP) |= -+ GPIO_bit(GPIO_NR_H5400_USB_PULLUP); -+ return; -+ } -+#endif -+#ifdef CONFIG_ARCH_INNOKOM -+ if (machine_is_innokom()) { -+ GPCR(GPIO_INNOKOM_USB_ONOFF) = GPIO_bit(GPIO_INNOKOM_USB_ONOFF); -+ printk("innokom: appear\n"); -+ udelay(5); -+ return; -+ } -+#endif -+#ifdef CONFIG_ARCH_CSB226 -+ if (machine_is_csb226()) { -+ GPDR0 |= 0x00000080; -+ GPSR0 |= 0x00000080; -+ printk("csb226: appear\n"); -+ udelay(5); -+ return; -+ } -+#endif -+ // lubbock has no D+ pullup -+#ifdef CONFIG_MACH_CORGI -+ if (machine_is_corgi()) { -+ GPDR(CORGI_CONNECT_GPIO) |= GPIO_bit(CORGI_CONNECT_GPIO); -+ GPSR(CORGI_CONNECT_GPIO) = GPIO_bit(CORGI_CONNECT_GPIO); -+ } -+#endif -+#ifdef CONFIG_MACH_POODLE -+ if (machine_is_poodle()) { -+ GPDR(POODLE_CONNECT_GPIO) |= GPIO_bit(POODLE_CONNECT_GPIO); -+ GPSR(POODLE_CONNECT_GPIO) = GPIO_bit(POODLE_CONNECT_GPIO); -+ } -+#endif -+ -+ if (!disc_first) { -+ pr_info("%s: can't force usb disconnect\n", driver_name); -+ disc_first++; -+ } -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* LEDs are only for debug */ -+#ifndef LED_CONNECTED_ON -+#define LED_CONNECTED_ON do {} while(0) -+#define LED_CONNECTED_OFF do {} while(0) -+#endif -+#ifndef LED_EP0_ON -+#define LED_EP0_ON do {} while (0) -+#define LED_EP0_OFF do {} while (0) -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Debugging support vanishes in non-debug builds. DBG_NORMAL should be -+ * mostly silent during normal use/testing, with no timing side-effects. -+ */ -+#define DBG_NORMAL 1 /* error paths, device state transitions */ -+#define DBG_VERBOSE 2 /* add some success path trace info */ -+#define DBG_NOISY 3 /* ... even more: request level */ -+#define DBG_VERY_NOISY 4 /* ... even more: packet level */ -+ -+#ifdef DEBUG -+ -+static const char *state_name[] = { -+ "EP0_IDLE", -+ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", -+ "EP0_END_XFER", "EP0_STALL" -+}; -+ -+#define DMSG(stuff...) printk(KERN_DEBUG "udc: " stuff) -+ -+#ifdef VERBOSE -+# define UDC_DEBUG DBG_VERBOSE -+#else -+# define UDC_DEBUG DBG_NORMAL -+#endif -+ -+static void __attribute__ ((__unused__)) -+dump_udccr(const char *label) -+{ -+ u32 udccr = UDCCR; -+ DMSG("%s %02X =%s%s%s%s%s%s%s%s\n", -+ label, udccr, -+ (udccr & UDCCR_REM) ? " rem" : "", -+ (udccr & UDCCR_RSTIR) ? " rstir" : "", -+ (udccr & UDCCR_SRM) ? " srm" : "", -+ (udccr & UDCCR_SUSIR) ? " susir" : "", -+ (udccr & UDCCR_RESIR) ? " resir" : "", -+ (udccr & UDCCR_RSM) ? " rsm" : "", -+ (udccr & UDCCR_UDA) ? " uda" : "", -+ (udccr & UDCCR_UDE) ? " ude" : ""); -+} -+ -+static void __attribute__ ((__unused__)) -+dump_udccs0(const char *label) -+{ -+ u32 udccs0 = UDCCS0; -+ -+ DMSG("%s %s %02X =%s%s%s%s%s%s%s%s\n", -+ label, state_name[the_controller->ep0state], udccs0, -+ (udccs0 & UDCCS0_SA) ? " sa" : "", -+ (udccs0 & UDCCS0_RNE) ? " rne" : "", -+ (udccs0 & UDCCS0_FST) ? " fst" : "", -+ (udccs0 & UDCCS0_SST) ? " sst" : "", -+ (udccs0 & UDCCS0_DRWF) ? " dwrf" : "", -+ (udccs0 & UDCCS0_FTF) ? " ftf" : "", -+ (udccs0 & UDCCS0_IPR) ? " ipr" : "", -+ (udccs0 & UDCCS0_OPR) ? " opr" : ""); -+} -+ -+static void __attribute__ ((__unused__)) -+dump_state(struct pxa2xx_udc *dev) -+{ -+ u32 tmp; -+ unsigned i; -+ -+ DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", -+ is_usb_connected() ? "host " : "disconnected", -+ state_name[dev->ep0state], -+ UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); -+ dump_udccr("udccr"); -+ if (dev->has_cfr) { -+ tmp = UDCCFR; -+ DMSG("udccfr %02X =%s%s\n", tmp, -+ (tmp & UDCCFR_AREN) ? " aren" : "", -+ (tmp & UDCCFR_ACM) ? " acm" : ""); -+ } -+ -+ if (!dev->driver) { -+ DMSG("no gadget driver bound\n"); -+ return; -+ } else -+ DMSG("ep0 driver '%s'\n", dev->driver->driver.name); -+ -+ if (!is_usb_connected()) -+ return; -+ -+ dump_udccs0 ("udccs0"); -+ DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", -+ dev->stats.write.bytes, dev->stats.write.ops, -+ dev->stats.read.bytes, dev->stats.read.ops); -+ -+ for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { -+ if (dev->ep [i].desc == 0) -+ continue; -+ DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs); -+ } -+} -+ -+#else -+ -+#define DMSG(stuff...) do{}while(0) -+ -+#define dump_udccr(x) do{}while(0) -+#define dump_udccs0(x) do{}while(0) -+#define dump_state(x) do{}while(0) -+ -+#define UDC_DEBUG ((unsigned)0) -+ -+#endif -+ -+#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) -+ -+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) -+#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) -+ -+ -+/* 2.4 backport support */ -+#define irqreturn_t void -+#define IRQ_HANDLED -+ -+ -+#endif /* __LINUX_USB_GADGET_PXA2XX_H */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/rndis.c kernel/drivers/usb/gadget/rndis.c ---- /tmp/kernel/drivers/usb/gadget/rndis.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/rndis.c 2005-04-22 17:53:19.501528608 +0200 -@@ -0,0 +1,1425 @@ -+/* -+ * RNDIS MSG parser -+ * -+ * Version: $Id: rndis.c,v 1.19 2004/03/25 21:33:46 robert Exp $ -+ * -+ * Authors: Benedikt Spranger, Pengutronix -+ * Robert Schwebel, Pengutronix -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This software was originally developed in conformance with -+ * Microsoft's Remote NDIS Specification License Agreement. -+ * -+ * 03/12/2004 Kai-Uwe Bloem <linux-development@auerswald.de> -+ * Fixed message length bug in init_response -+ * -+ * 03/25/2004 Kai-Uwe Bloem <linux-development@auerswald.de> -+ * Fixed rndis_rm_hdr length bug. -+ * -+ * Copyright (C) 2004 by David Brownell -+ * updates to merge with Linux 2.6, better match RNDIS spec -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/moduleparam.h> -+#include <linux/kernel.h> -+#include <linux/errno.h> -+#include <linux/version.h> -+#include <linux/init.h> -+#include <linux/list.h> -+#include <linux/proc_fs.h> -+#include <linux/netdevice.h> -+ -+#include <asm/io.h> -+#include <asm/byteorder.h> -+#include <asm/system.h> -+ -+ -+#undef RNDIS_PM -+#undef VERBOSE -+ -+#include "rndis.h" -+ -+ -+/* The driver for your USB chip needs to support ep0 OUT to work with -+ * RNDIS, plus all three CDC Ethernet endpoints (interrupt not optional). -+ * -+ * Windows hosts need an INF file like Documentation/usb/linux.inf -+ * and will be happier if you provide the host_addr module parameter. -+ */ -+ -+#if 0 -+#define DEBUG(str,args...) do { \ -+ if (rndis_debug) \ -+ printk(KERN_DEBUG str , ## args ); \ -+ } while (0) -+static int rndis_debug = 0; -+ -+module_param (rndis_debug, bool, 0); -+MODULE_PARM_DESC (rndis_debug, "enable debugging"); -+ -+#else -+ -+#define rndis_debug 0 -+#define DEBUG(str,args...) do{}while(0) -+#endif -+ -+#define RNDIS_MAX_CONFIGS 1 -+ -+ -+static rndis_params rndis_per_dev_params [RNDIS_MAX_CONFIGS]; -+ -+/* Driver Version */ -+static const u32 rndis_driver_version = __constant_cpu_to_le32 (1); -+ -+/* Function Prototypes */ -+static int rndis_init_response (int configNr, rndis_init_msg_type *buf); -+static int rndis_query_response (int configNr, rndis_query_msg_type *buf); -+static int rndis_set_response (int configNr, rndis_set_msg_type *buf); -+static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf); -+static int rndis_keepalive_response (int configNr, -+ rndis_keepalive_msg_type *buf); -+ -+static rndis_resp_t *rndis_add_response (int configNr, u32 length); -+ -+ -+/* NDIS Functions */ -+static int gen_ndis_query_resp (int configNr, u32 OID, rndis_resp_t *r) -+{ -+ int retval = -ENOTSUPP; -+ u32 length = 0; -+ u32 *tmp; -+ int i, count; -+ rndis_query_cmplt_type *resp; -+ -+ if (!r) return -ENOMEM; -+ resp = (rndis_query_cmplt_type *) r->buf; -+ -+ if (!resp) return -ENOMEM; -+ -+ switch (OID) { -+ -+ /* general oids (table 4-1) */ -+ -+ /* mandatory */ -+ case OID_GEN_SUPPORTED_LIST: -+ DEBUG ("%s: OID_GEN_SUPPORTED_LIST\n", __FUNCTION__); -+ length = sizeof (oid_supported_list); -+ count = length / sizeof (u32); -+ tmp = (u32 *) ((u8 *)resp + 24); -+ for (i = 0; i < count; i++) -+ tmp[i] = cpu_to_le32 (oid_supported_list[i]); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_HARDWARE_STATUS: -+ DEBUG("%s: OID_GEN_HARDWARE_STATUS\n", __FUNCTION__); -+ length = 4; -+ /* Bogus question! -+ * Hardware must be ready to receive high level protocols. -+ * BTW: -+ * reddite ergo quae sunt Caesaris Caesari -+ * et quae sunt Dei Deo! -+ */ -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_MEDIA_SUPPORTED: -+ DEBUG("%s: OID_GEN_MEDIA_SUPPORTED\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].medium); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_MEDIA_IN_USE: -+ DEBUG("%s: OID_GEN_MEDIA_IN_USE\n", __FUNCTION__); -+ length = 4; -+ /* one medium, one transport... (maybe you do it better) */ -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].medium); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_MAXIMUM_FRAME_SIZE: -+ DEBUG("%s: OID_GEN_MAXIMUM_FRAME_SIZE\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].dev) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].dev->mtu); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_LINK_SPEED: -+ DEBUG("%s: OID_GEN_LINK_SPEED\n", __FUNCTION__); -+ length = 4; -+ if (rndis_per_dev_params [configNr].media_state -+ == NDIS_MEDIA_STATE_DISCONNECTED) -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ else -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].speed); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_TRANSMIT_BLOCK_SIZE: -+ DEBUG("%s: OID_GEN_TRANSMIT_BLOCK_SIZE\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].dev) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].dev->mtu); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_RECEIVE_BLOCK_SIZE: -+ DEBUG("%s: OID_GEN_RECEIVE_BLOCK_SIZE\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].dev) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].dev->mtu); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_VENDOR_ID: -+ DEBUG("%s: OID_GEN_VENDOR_ID\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].vendorID); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_VENDOR_DESCRIPTION: -+ DEBUG("%s: OID_GEN_VENDOR_DESCRIPTION\n", __FUNCTION__); -+ length = strlen (rndis_per_dev_params [configNr].vendorDescr); -+ memcpy ((u8 *) resp + 24, -+ rndis_per_dev_params [configNr].vendorDescr, length); -+ retval = 0; -+ break; -+ -+ case OID_GEN_VENDOR_DRIVER_VERSION: -+ DEBUG("%s: OID_GEN_VENDOR_DRIVER_VERSION\n", __FUNCTION__); -+ length = 4; -+ /* Created as LE */ -+ *((u32 *) resp + 6) = rndis_driver_version; -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_CURRENT_PACKET_FILTER: -+ DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params[configNr].filter); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_MAXIMUM_TOTAL_SIZE: -+ DEBUG("%s: OID_GEN_MAXIMUM_TOTAL_SIZE\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = __constant_cpu_to_le32( -+ RNDIS_MAX_TOTAL_SIZE); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_MEDIA_CONNECT_STATUS: -+ DEBUG("%s: OID_GEN_MEDIA_CONNECT_STATUS\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .media_state); -+ retval = 0; -+ break; -+ -+ case OID_GEN_PHYSICAL_MEDIUM: -+ DEBUG("%s: OID_GEN_PHYSICAL_MEDIUM\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ break; -+ -+ /* The RNDIS specification is incomplete/wrong. Some versions -+ * of MS-Windows expect OIDs that aren't specified there. Other -+ * versions emit undefined RNDIS messages. DOCUMENT ALL THESE! -+ */ -+ case OID_GEN_MAC_OPTIONS: /* from WinME */ -+ DEBUG("%s: OID_GEN_MAC_OPTIONS\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = __constant_cpu_to_le32( -+ NDIS_MAC_OPTION_RECEIVE_SERIALIZED -+ | NDIS_MAC_OPTION_FULL_DUPLEX); -+ retval = 0; -+ break; -+ -+ /* statistics OIDs (table 4-2) */ -+ -+ /* mandatory */ -+ case OID_GEN_XMIT_OK: -+ DEBUG("%s: OID_GEN_XMIT_OK\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].stats->tx_packets - -+ rndis_per_dev_params [configNr].stats->tx_errors - -+ rndis_per_dev_params [configNr].stats->tx_dropped); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_RCV_OK: -+ DEBUG("%s: OID_GEN_RCV_OK\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].stats->rx_packets - -+ rndis_per_dev_params [configNr].stats->rx_errors - -+ rndis_per_dev_params [configNr].stats->rx_dropped); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_XMIT_ERROR: -+ DEBUG("%s: OID_GEN_XMIT_ERROR\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->tx_errors); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_RCV_ERROR: -+ DEBUG("%s: OID_GEN_RCV_ERROR\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->rx_errors); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_GEN_RCV_NO_BUFFER: -+ DEBUG("%s: OID_GEN_RCV_NO_BUFFER\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->rx_dropped); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+#ifdef RNDIS_OPTIONAL_STATS -+ case OID_GEN_DIRECTED_BYTES_XMIT: -+ DEBUG("%s: OID_GEN_DIRECTED_BYTES_XMIT\n", __FUNCTION__); -+ /* -+ * Aunt Tilly's size of shoes -+ * minus antarctica count of penguins -+ * divided by weight of Alpha Centauri -+ */ -+ if (rndis_per_dev_params [configNr].stats) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ (rndis_per_dev_params [configNr] -+ .stats->tx_packets - -+ rndis_per_dev_params [configNr] -+ .stats->tx_errors - -+ rndis_per_dev_params [configNr] -+ .stats->tx_dropped) -+ * 123); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_DIRECTED_FRAMES_XMIT: -+ DEBUG("%s: OID_GEN_DIRECTED_FRAMES_XMIT\n", __FUNCTION__); -+ /* dito */ -+ if (rndis_per_dev_params [configNr].stats) { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ (rndis_per_dev_params [configNr] -+ .stats->tx_packets - -+ rndis_per_dev_params [configNr] -+ .stats->tx_errors - -+ rndis_per_dev_params [configNr] -+ .stats->tx_dropped) -+ / 123); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_MULTICAST_BYTES_XMIT: -+ DEBUG("%s: OID_GEN_MULTICAST_BYTES_XMIT\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->multicast*1234); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_MULTICAST_FRAMES_XMIT: -+ DEBUG("%s: OID_GEN_MULTICAST_FRAMES_XMIT\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->multicast); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_BROADCAST_BYTES_XMIT: -+ DEBUG("%s: OID_GEN_BROADCAST_BYTES_XMIT\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->tx_packets/42*255); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_BROADCAST_FRAMES_XMIT: -+ DEBUG("%s: OID_GEN_BROADCAST_FRAMES_XMIT\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->tx_packets/42); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_DIRECTED_BYTES_RCV: -+ DEBUG("%s: OID_GEN_DIRECTED_BYTES_RCV\n", __FUNCTION__); -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ break; -+ -+ case OID_GEN_DIRECTED_FRAMES_RCV: -+ DEBUG("%s: OID_GEN_DIRECTED_FRAMES_RCV\n", __FUNCTION__); -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ break; -+ -+ case OID_GEN_MULTICAST_BYTES_RCV: -+ DEBUG("%s: OID_GEN_MULTICAST_BYTES_RCV\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->multicast * 1111); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_MULTICAST_FRAMES_RCV: -+ DEBUG("%s: OID_GEN_MULTICAST_FRAMES_RCV\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->multicast); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_BROADCAST_BYTES_RCV: -+ DEBUG("%s: OID_GEN_BROADCAST_BYTES_RCV\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->rx_packets/42*255); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_BROADCAST_FRAMES_RCV: -+ DEBUG("%s: OID_GEN_BROADCAST_FRAMES_RCV\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->rx_packets/42); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_RCV_CRC_ERROR: -+ DEBUG("%s: OID_GEN_RCV_CRC_ERROR\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) { -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->rx_crc_errors); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ case OID_GEN_TRANSMIT_QUEUE_LENGTH: -+ DEBUG("%s: OID_GEN_TRANSMIT_QUEUE_LENGTH\n", __FUNCTION__); -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ break; -+#endif /* RNDIS_OPTIONAL_STATS */ -+ -+ /* ieee802.3 OIDs (table 4-3) */ -+ -+ /* mandatory */ -+ case OID_802_3_PERMANENT_ADDRESS: -+ DEBUG("%s: OID_802_3_PERMANENT_ADDRESS\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].dev) { -+ length = ETH_ALEN; -+ memcpy ((u8 *) resp + 24, -+ rndis_per_dev_params [configNr].host_mac, -+ length); -+ retval = 0; -+ } else { -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_802_3_CURRENT_ADDRESS: -+ DEBUG("%s: OID_802_3_CURRENT_ADDRESS\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].dev) { -+ length = ETH_ALEN; -+ memcpy ((u8 *) resp + 24, -+ rndis_per_dev_params [configNr].host_mac, -+ length); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_802_3_MULTICAST_LIST: -+ DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); -+ length = 4; -+ /* Multicast base address only */ -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0xE0000000); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_802_3_MAXIMUM_LIST_SIZE: -+ DEBUG("%s: OID_802_3_MAXIMUM_LIST_SIZE\n", __FUNCTION__); -+ length = 4; -+ /* Multicast base address only */ -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (1); -+ retval = 0; -+ break; -+ -+ case OID_802_3_MAC_OPTIONS: -+ DEBUG("%s: OID_802_3_MAC_OPTIONS\n", __FUNCTION__); -+ break; -+ -+ /* ieee802.3 statistics OIDs (table 4-4) */ -+ -+ /* mandatory */ -+ case OID_802_3_RCV_ERROR_ALIGNMENT: -+ DEBUG("%s: OID_802_3_RCV_ERROR_ALIGNMENT\n", __FUNCTION__); -+ if (rndis_per_dev_params [configNr].stats) -+ { -+ length = 4; -+ *((u32 *) resp + 6) = cpu_to_le32 ( -+ rndis_per_dev_params [configNr] -+ .stats->rx_frame_errors); -+ retval = 0; -+ } -+ break; -+ -+ /* mandatory */ -+ case OID_802_3_XMIT_ONE_COLLISION: -+ DEBUG("%s: OID_802_3_XMIT_ONE_COLLISION\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ break; -+ -+ /* mandatory */ -+ case OID_802_3_XMIT_MORE_COLLISIONS: -+ DEBUG("%s: OID_802_3_XMIT_MORE_COLLISIONS\n", __FUNCTION__); -+ length = 4; -+ *((u32 *) resp + 6) = __constant_cpu_to_le32 (0); -+ retval = 0; -+ break; -+ -+#ifdef RNDIS_OPTIONAL_STATS -+ case OID_802_3_XMIT_DEFERRED: -+ DEBUG("%s: OID_802_3_XMIT_DEFERRED\n", __FUNCTION__); -+ /* TODO */ -+ break; -+ -+ case OID_802_3_XMIT_MAX_COLLISIONS: -+ DEBUG("%s: OID_802_3_XMIT_MAX_COLLISIONS\n", __FUNCTION__); -+ /* TODO */ -+ break; -+ -+ case OID_802_3_RCV_OVERRUN: -+ DEBUG("%s: OID_802_3_RCV_OVERRUN\n", __FUNCTION__); -+ /* TODO */ -+ break; -+ -+ case OID_802_3_XMIT_UNDERRUN: -+ DEBUG("%s: OID_802_3_XMIT_UNDERRUN\n", __FUNCTION__); -+ /* TODO */ -+ break; -+ -+ case OID_802_3_XMIT_HEARTBEAT_FAILURE: -+ DEBUG("%s: OID_802_3_XMIT_HEARTBEAT_FAILURE\n", __FUNCTION__); -+ /* TODO */ -+ break; -+ -+ case OID_802_3_XMIT_TIMES_CRS_LOST: -+ DEBUG("%s: OID_802_3_XMIT_TIMES_CRS_LOST\n", __FUNCTION__); -+ /* TODO */ -+ break; -+ -+ case OID_802_3_XMIT_LATE_COLLISIONS: -+ DEBUG("%s: OID_802_3_XMIT_LATE_COLLISIONS\n", __FUNCTION__); -+ /* TODO */ -+ break; -+#endif /* RNDIS_OPTIONAL_STATS */ -+ -+#ifdef RNDIS_PM -+ /* power management OIDs (table 4-5) */ -+ case OID_PNP_CAPABILITIES: -+ DEBUG("%s: OID_PNP_CAPABILITIES\n", __FUNCTION__); -+ -+ /* just PM, and remote wakeup on link status change -+ * (not magic packet or pattern match) -+ */ -+ length = sizeof (struct NDIS_PNP_CAPABILITIES); -+ memset (resp, 0, length); -+ { -+ struct NDIS_PNP_CAPABILITIES *caps = (void *) resp; -+ -+ caps->Flags = NDIS_DEVICE_WAKE_UP_ENABLE; -+ caps->WakeUpCapabilities.MinLinkChangeWakeUp -+ = NdisDeviceStateD3; -+ -+ /* FIXME then use usb_gadget_wakeup(), and -+ * set USB_CONFIG_ATT_WAKEUP in config desc -+ */ -+ } -+ retval = 0; -+ break; -+ case OID_PNP_QUERY_POWER: -+ DEBUG("%s: OID_PNP_QUERY_POWER\n", __FUNCTION__); -+ /* sure, handle any power state that maps to USB suspend */ -+ retval = 0; -+ break; -+#endif -+ -+ default: -+ printk (KERN_WARNING "%s: query unknown OID 0x%08X\n", -+ __FUNCTION__, OID); -+ } -+ -+ resp->InformationBufferOffset = __constant_cpu_to_le32 (16); -+ resp->InformationBufferLength = cpu_to_le32 (length); -+ resp->MessageLength = cpu_to_le32 (24 + length); -+ r->length = 24 + length; -+ return retval; -+} -+ -+static int gen_ndis_set_resp (u8 configNr, u32 OID, u8 *buf, u32 buf_len, -+ rndis_resp_t *r) -+{ -+ rndis_set_cmplt_type *resp; -+ int i, retval = -ENOTSUPP; -+ struct rndis_params *params; -+ -+ if (!r) -+ return -ENOMEM; -+ resp = (rndis_set_cmplt_type *) r->buf; -+ if (!resp) -+ return -ENOMEM; -+ -+ DEBUG("set OID %08x value, len %d:\n", OID, buf_len); -+ for (i = 0; i < buf_len; i += 16) { -+ DEBUG ("%03d: " -+ " %02x %02x %02x %02x" -+ " %02x %02x %02x %02x" -+ " %02x %02x %02x %02x" -+ " %02x %02x %02x %02x" -+ "\n", -+ i, -+ buf[i], buf [i+1], -+ buf[i+2], buf[i+3], -+ buf[i+4], buf [i+5], -+ buf[i+6], buf[i+7], -+ buf[i+8], buf [i+9], -+ buf[i+10], buf[i+11], -+ buf[i+12], buf [i+13], -+ buf[i+14], buf[i+15]); -+ } -+ -+ switch (OID) { -+ case OID_GEN_CURRENT_PACKET_FILTER: -+ params = &rndis_per_dev_params [configNr]; -+ retval = 0; -+ -+ /* FIXME use these NDIS_PACKET_TYPE_* bitflags to -+ * filter packets in hard_start_xmit() -+ * NDIS_PACKET_TYPE_x == USB_CDC_PACKET_TYPE_x for x in: -+ * PROMISCUOUS, DIRECTED, -+ * MULTICAST, ALL_MULTICAST, BROADCAST -+ */ -+ params->filter = cpu_to_le32p((u32 *)buf); -+ DEBUG("%s: OID_GEN_CURRENT_PACKET_FILTER %08x\n", -+ __FUNCTION__, params->filter); -+ -+ /* this call has a significant side effect: it's -+ * what makes the packet flow start and stop, like -+ * activating the CDC Ethernet altsetting. -+ */ -+ if (params->filter) { -+ params->state = RNDIS_DATA_INITIALIZED; -+ netif_carrier_on(params->dev); -+ if (netif_running(params->dev)) -+ netif_wake_queue (params->dev); -+ } else { -+ params->state = RNDIS_INITIALIZED; -+ netif_carrier_off (params->dev); -+ netif_stop_queue (params->dev); -+ } -+ break; -+ -+ case OID_802_3_MULTICAST_LIST: -+ /* I think we can ignore this */ -+ DEBUG("%s: OID_802_3_MULTICAST_LIST\n", __FUNCTION__); -+ retval = 0; -+ break; -+#if 0 -+ case OID_GEN_RNDIS_CONFIG_PARAMETER: -+ { -+ struct rndis_config_parameter *param; -+ param = (struct rndis_config_parameter *) buf; -+ DEBUG("%s: OID_GEN_RNDIS_CONFIG_PARAMETER '%*s'\n", -+ __FUNCTION__, -+ min(cpu_to_le32(param->ParameterNameLength),80), -+ buf + param->ParameterNameOffset); -+ retval = 0; -+ } -+ break; -+#endif -+ -+#ifdef RNDIS_PM -+ case OID_PNP_SET_POWER: -+ DEBUG ("OID_PNP_SET_POWER\n"); -+ /* sure, handle any power state that maps to USB suspend */ -+ retval = 0; -+ break; -+ -+ case OID_PNP_ENABLE_WAKE_UP: -+ /* always-connected ... */ -+ DEBUG ("OID_PNP_ENABLE_WAKE_UP\n"); -+ retval = 0; -+ break; -+ -+ // no PM resume patterns supported (specified where?) -+ // so OID_PNP_{ADD,REMOVE}_WAKE_UP_PATTERN always fails -+#endif -+ -+ default: -+ printk (KERN_WARNING "%s: set unknown OID 0x%08X, size %d\n", -+ __FUNCTION__, OID, buf_len); -+ } -+ -+ return retval; -+} -+ -+/* -+ * Response Functions -+ */ -+ -+static int rndis_init_response (int configNr, rndis_init_msg_type *buf) -+{ -+ rndis_init_cmplt_type *resp; -+ rndis_resp_t *r; -+ -+ if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; -+ -+ r = rndis_add_response (configNr, sizeof (rndis_init_cmplt_type)); -+ -+ if (!r) return -ENOMEM; -+ -+ resp = (rndis_init_cmplt_type *) r->buf; -+ -+ if (!resp) return -ENOMEM; -+ -+ resp->MessageType = __constant_cpu_to_le32 ( -+ REMOTE_NDIS_INITIALIZE_CMPLT); -+ resp->MessageLength = __constant_cpu_to_le32 (52); -+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ -+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); -+ resp->MajorVersion = __constant_cpu_to_le32 (RNDIS_MAJOR_VERSION); -+ resp->MinorVersion = __constant_cpu_to_le32 (RNDIS_MINOR_VERSION); -+ resp->DeviceFlags = __constant_cpu_to_le32 (RNDIS_DF_CONNECTIONLESS); -+ resp->Medium = __constant_cpu_to_le32 (RNDIS_MEDIUM_802_3); -+ resp->MaxPacketsPerTransfer = __constant_cpu_to_le32 (1); -+ resp->MaxTransferSize = cpu_to_le32 ( -+ rndis_per_dev_params [configNr].dev->mtu -+ + sizeof (struct ethhdr) -+ + sizeof (struct rndis_packet_msg_type) -+ + 22); -+ resp->PacketAlignmentFactor = __constant_cpu_to_le32 (0); -+ resp->AFListOffset = __constant_cpu_to_le32 (0); -+ resp->AFListSize = __constant_cpu_to_le32 (0); -+ -+ if (rndis_per_dev_params [configNr].ack) -+ rndis_per_dev_params [configNr].ack ( -+ rndis_per_dev_params [configNr].dev); -+ -+ return 0; -+} -+ -+static int rndis_query_response (int configNr, rndis_query_msg_type *buf) -+{ -+ rndis_query_cmplt_type *resp; -+ rndis_resp_t *r; -+ -+ // DEBUG("%s: OID = %08X\n", __FUNCTION__, cpu_to_le32(buf->OID)); -+ if (!rndis_per_dev_params [configNr].dev) return -ENOTSUPP; -+ -+ /* -+ * we need more memory: -+ * oid_supported_list is the largest answer -+ */ -+ r = rndis_add_response (configNr, sizeof (oid_supported_list)); -+ -+ if (!r) return -ENOMEM; -+ resp = (rndis_query_cmplt_type *) r->buf; -+ -+ if (!resp) return -ENOMEM; -+ -+ resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_QUERY_CMPLT); -+ resp->MessageLength = __constant_cpu_to_le32 (24); -+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ -+ -+ if (gen_ndis_query_resp (configNr, cpu_to_le32 (buf->OID), r)) { -+ /* OID not supported */ -+ resp->Status = __constant_cpu_to_le32 ( -+ RNDIS_STATUS_NOT_SUPPORTED); -+ resp->InformationBufferLength = __constant_cpu_to_le32 (0); -+ resp->InformationBufferOffset = __constant_cpu_to_le32 (0); -+ } else -+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); -+ -+ if (rndis_per_dev_params [configNr].ack) -+ rndis_per_dev_params [configNr].ack ( -+ rndis_per_dev_params [configNr].dev); -+ return 0; -+} -+ -+static int rndis_set_response (int configNr, rndis_set_msg_type *buf) -+{ -+ u32 BufLength, BufOffset; -+ rndis_set_cmplt_type *resp; -+ rndis_resp_t *r; -+ -+ r = rndis_add_response (configNr, sizeof (rndis_set_cmplt_type)); -+ -+ if (!r) return -ENOMEM; -+ resp = (rndis_set_cmplt_type *) r->buf; -+ if (!resp) return -ENOMEM; -+ -+ BufLength = cpu_to_le32 (buf->InformationBufferLength); -+ BufOffset = cpu_to_le32 (buf->InformationBufferOffset); -+ -+#ifdef VERBOSE -+ DEBUG("%s: Length: %d\n", __FUNCTION__, BufLength); -+ DEBUG("%s: Offset: %d\n", __FUNCTION__, BufOffset); -+ DEBUG("%s: InfoBuffer: ", __FUNCTION__); -+ -+ for (i = 0; i < BufLength; i++) { -+ DEBUG ("%02x ", *(((u8 *) buf) + i + 8 + BufOffset)); -+ } -+ -+ DEBUG ("\n"); -+#endif -+ -+ resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_SET_CMPLT); -+ resp->MessageLength = __constant_cpu_to_le32 (16); -+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ -+ if (gen_ndis_set_resp (configNr, cpu_to_le32 (buf->OID), -+ ((u8 *) buf) + 8 + BufOffset, BufLength, r)) -+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_NOT_SUPPORTED); -+ else resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); -+ -+ if (rndis_per_dev_params [configNr].ack) -+ rndis_per_dev_params [configNr].ack ( -+ rndis_per_dev_params [configNr].dev); -+ -+ return 0; -+} -+ -+static int rndis_reset_response (int configNr, rndis_reset_msg_type *buf) -+{ -+ rndis_reset_cmplt_type *resp; -+ rndis_resp_t *r; -+ -+ r = rndis_add_response (configNr, sizeof (rndis_reset_cmplt_type)); -+ -+ if (!r) return -ENOMEM; -+ resp = (rndis_reset_cmplt_type *) r->buf; -+ if (!resp) return -ENOMEM; -+ -+ resp->MessageType = __constant_cpu_to_le32 (REMOTE_NDIS_RESET_CMPLT); -+ resp->MessageLength = __constant_cpu_to_le32 (16); -+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); -+ /* resent information */ -+ resp->AddressingReset = __constant_cpu_to_le32 (1); -+ -+ if (rndis_per_dev_params [configNr].ack) -+ rndis_per_dev_params [configNr].ack ( -+ rndis_per_dev_params [configNr].dev); -+ -+ return 0; -+} -+ -+static int rndis_keepalive_response (int configNr, -+ rndis_keepalive_msg_type *buf) -+{ -+ rndis_keepalive_cmplt_type *resp; -+ rndis_resp_t *r; -+ -+ /* host "should" check only in RNDIS_DATA_INITIALIZED state */ -+ -+ r = rndis_add_response (configNr, sizeof (rndis_keepalive_cmplt_type)); -+ resp = (rndis_keepalive_cmplt_type *) r->buf; -+ if (!resp) return -ENOMEM; -+ -+ resp->MessageType = __constant_cpu_to_le32 ( -+ REMOTE_NDIS_KEEPALIVE_CMPLT); -+ resp->MessageLength = __constant_cpu_to_le32 (16); -+ resp->RequestID = buf->RequestID; /* Still LE in msg buffer */ -+ resp->Status = __constant_cpu_to_le32 (RNDIS_STATUS_SUCCESS); -+ -+ if (rndis_per_dev_params [configNr].ack) -+ rndis_per_dev_params [configNr].ack ( -+ rndis_per_dev_params [configNr].dev); -+ -+ return 0; -+} -+ -+ -+/* -+ * Device to Host Comunication -+ */ -+static int rndis_indicate_status_msg (int configNr, u32 status) -+{ -+ rndis_indicate_status_msg_type *resp; -+ rndis_resp_t *r; -+ -+ if (rndis_per_dev_params [configNr].state == RNDIS_UNINITIALIZED) -+ return -ENOTSUPP; -+ -+ r = rndis_add_response (configNr, -+ sizeof (rndis_indicate_status_msg_type)); -+ if (!r) return -ENOMEM; -+ -+ resp = (rndis_indicate_status_msg_type *) r->buf; -+ if (!resp) return -ENOMEM; -+ -+ resp->MessageType = __constant_cpu_to_le32 ( -+ REMOTE_NDIS_INDICATE_STATUS_MSG); -+ resp->MessageLength = __constant_cpu_to_le32 (20); -+ resp->Status = cpu_to_le32 (status); -+ resp->StatusBufferLength = __constant_cpu_to_le32 (0); -+ resp->StatusBufferOffset = __constant_cpu_to_le32 (0); -+ -+ if (rndis_per_dev_params [configNr].ack) -+ rndis_per_dev_params [configNr].ack ( -+ rndis_per_dev_params [configNr].dev); -+ return 0; -+} -+ -+int rndis_signal_connect (int configNr) -+{ -+ rndis_per_dev_params [configNr].media_state -+ = NDIS_MEDIA_STATE_CONNECTED; -+ return rndis_indicate_status_msg (configNr, -+ RNDIS_STATUS_MEDIA_CONNECT); -+} -+ -+int rndis_signal_disconnect (int configNr) -+{ -+ rndis_per_dev_params [configNr].media_state -+ = NDIS_MEDIA_STATE_DISCONNECTED; -+ return rndis_indicate_status_msg (configNr, -+ RNDIS_STATUS_MEDIA_DISCONNECT); -+} -+ -+void rndis_set_host_mac (int configNr, const u8 *addr) -+{ -+ rndis_per_dev_params [configNr].host_mac = addr; -+} -+ -+/* -+ * Message Parser -+ */ -+int rndis_msg_parser (u8 configNr, u8 *buf) -+{ -+ u32 MsgType, MsgLength, *tmp; -+ struct rndis_params *params; -+ -+ if (!buf) -+ return -ENOMEM; -+ -+ tmp = (u32 *) buf; -+ MsgType = cpu_to_le32p(tmp++); -+ MsgLength = cpu_to_le32p(tmp++); -+ -+ if (configNr >= RNDIS_MAX_CONFIGS) -+ return -ENOTSUPP; -+ params = &rndis_per_dev_params [configNr]; -+ -+ /* For USB: responses may take up to 10 seconds */ -+ switch (MsgType) -+ { -+ case REMOTE_NDIS_INITIALIZE_MSG: -+ DEBUG("%s: REMOTE_NDIS_INITIALIZE_MSG\n", -+ __FUNCTION__ ); -+ params->state = RNDIS_INITIALIZED; -+ return rndis_init_response (configNr, -+ (rndis_init_msg_type *) buf); -+ -+ case REMOTE_NDIS_HALT_MSG: -+ DEBUG("%s: REMOTE_NDIS_HALT_MSG\n", -+ __FUNCTION__ ); -+ params->state = RNDIS_UNINITIALIZED; -+ if (params->dev) { -+ netif_carrier_off (params->dev); -+ netif_stop_queue (params->dev); -+ } -+ return 0; -+ -+ case REMOTE_NDIS_QUERY_MSG: -+ return rndis_query_response (configNr, -+ (rndis_query_msg_type *) buf); -+ -+ case REMOTE_NDIS_SET_MSG: -+ return rndis_set_response (configNr, -+ (rndis_set_msg_type *) buf); -+ -+ case REMOTE_NDIS_RESET_MSG: -+ DEBUG("%s: REMOTE_NDIS_RESET_MSG\n", -+ __FUNCTION__ ); -+ return rndis_reset_response (configNr, -+ (rndis_reset_msg_type *) buf); -+ -+ case REMOTE_NDIS_KEEPALIVE_MSG: -+ /* For USB: host does this every 5 seconds */ -+#ifdef VERBOSE -+ DEBUG("%s: REMOTE_NDIS_KEEPALIVE_MSG\n", -+ __FUNCTION__ ); -+#endif -+ return rndis_keepalive_response (configNr, -+ (rndis_keepalive_msg_type *) -+ buf); -+ -+ default: -+ /* At least Windows XP emits some undefined RNDIS messages. -+ * In one case those messages seemed to relate to the host -+ * suspending itself. -+ */ -+ printk (KERN_WARNING -+ "%s: unknown RNDIS message 0x%08X len %d\n", -+ __FUNCTION__ , MsgType, MsgLength); -+ { -+ unsigned i; -+ for (i = 0; i < MsgLength; i += 16) { -+ DEBUG ("%03d: " -+ " %02x %02x %02x %02x" -+ " %02x %02x %02x %02x" -+ " %02x %02x %02x %02x" -+ " %02x %02x %02x %02x" -+ "\n", -+ i, -+ buf[i], buf [i+1], -+ buf[i+2], buf[i+3], -+ buf[i+4], buf [i+5], -+ buf[i+6], buf[i+7], -+ buf[i+8], buf [i+9], -+ buf[i+10], buf[i+11], -+ buf[i+12], buf [i+13], -+ buf[i+14], buf[i+15]); -+ } -+ } -+ break; -+ } -+ -+ return -ENOTSUPP; -+} -+ -+int rndis_register (int (* rndis_control_ack) (struct net_device *)) -+{ -+ u8 i; -+ -+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -+ if (!rndis_per_dev_params [i].used) { -+ rndis_per_dev_params [i].used = 1; -+ rndis_per_dev_params [i].ack = rndis_control_ack; -+ DEBUG("%s: configNr = %d\n", __FUNCTION__, i); -+ return i; -+ } -+ } -+ DEBUG("failed\n"); -+ -+ return -1; -+} -+ -+void rndis_deregister (int configNr) -+{ -+ DEBUG("%s: \n", __FUNCTION__ ); -+ -+ if (configNr >= RNDIS_MAX_CONFIGS) return; -+ rndis_per_dev_params [configNr].used = 0; -+ -+ return; -+} -+ -+int rndis_set_param_dev (u8 configNr, struct net_device *dev, -+ struct net_device_stats *stats) -+{ -+ DEBUG("%s:\n", __FUNCTION__ ); -+ if (!dev || !stats) return -1; -+ if (configNr >= RNDIS_MAX_CONFIGS) return -1; -+ -+ rndis_per_dev_params [configNr].dev = dev; -+ rndis_per_dev_params [configNr].stats = stats; -+ -+ return 0; -+} -+ -+int rndis_set_param_vendor (u8 configNr, u32 vendorID, const char *vendorDescr) -+{ -+ DEBUG("%s:\n", __FUNCTION__ ); -+ if (!vendorDescr) return -1; -+ if (configNr >= RNDIS_MAX_CONFIGS) return -1; -+ -+ rndis_per_dev_params [configNr].vendorID = vendorID; -+ rndis_per_dev_params [configNr].vendorDescr = vendorDescr; -+ -+ return 0; -+} -+ -+int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed) -+{ -+ DEBUG("%s:\n", __FUNCTION__ ); -+ if (configNr >= RNDIS_MAX_CONFIGS) return -1; -+ -+ rndis_per_dev_params [configNr].medium = medium; -+ rndis_per_dev_params [configNr].speed = speed; -+ -+ return 0; -+} -+ -+void rndis_add_hdr (struct sk_buff *skb) -+{ -+ if (!skb) return; -+ skb_push (skb, sizeof (struct rndis_packet_msg_type)); -+ memset (skb->data, 0, sizeof (struct rndis_packet_msg_type)); -+ *((u32 *) skb->data) = __constant_cpu_to_le32 (1); -+ *((u32 *) skb->data + 1) = cpu_to_le32(skb->len); -+ *((u32 *) skb->data + 2) = __constant_cpu_to_le32 (36); -+ *((u32 *) skb->data + 3) = cpu_to_le32(skb->len - 44); -+ -+ return; -+} -+ -+void rndis_free_response (int configNr, u8 *buf) -+{ -+ rndis_resp_t *r; -+ struct list_head *act, *tmp; -+ -+ list_for_each_safe (act, tmp, -+ &(rndis_per_dev_params [configNr].resp_queue)) -+ { -+ r = list_entry (act, rndis_resp_t, list); -+ if (r && r->buf == buf) { -+ list_del (&r->list); -+ kfree (r); -+ } -+ } -+} -+ -+u8 *rndis_get_next_response (int configNr, u32 *length) -+{ -+ rndis_resp_t *r; -+ struct list_head *act, *tmp; -+ -+ if (!length) return NULL; -+ -+ list_for_each_safe (act, tmp, -+ &(rndis_per_dev_params [configNr].resp_queue)) -+ { -+ r = list_entry (act, rndis_resp_t, list); -+ if (!r->send) { -+ r->send = 1; -+ *length = r->length; -+ return r->buf; -+ } -+ } -+ -+ return NULL; -+} -+ -+static rndis_resp_t *rndis_add_response (int configNr, u32 length) -+{ -+ rndis_resp_t *r; -+ -+ r = kmalloc (sizeof (rndis_resp_t) + length, GFP_ATOMIC); -+ if (!r) return NULL; -+ -+ r->buf = (u8 *) (r + 1); -+ r->length = length; -+ r->send = 0; -+ -+ list_add_tail (&r->list, -+ &(rndis_per_dev_params [configNr].resp_queue)); -+ return r; -+} -+ -+int rndis_rm_hdr (u8 *buf, u32 *length) -+{ -+ u32 i, messageLen, dataOffset, *tmp; -+ -+ tmp = (u32 *) buf; -+ -+ if (!buf || !length) return -1; -+ if (cpu_to_le32p(tmp++) != 1) return -1; -+ -+ messageLen = cpu_to_le32p(tmp++); -+ dataOffset = cpu_to_le32p(tmp++) + 8; -+ -+ if (messageLen < dataOffset || messageLen > *length) return -1; -+ -+ for (i = dataOffset; i < messageLen; i++) -+ buf [i - dataOffset] = buf [i]; -+ -+ *length = messageLen - dataOffset; -+ -+ return 0; -+} -+ -+#ifdef CONFIG_USB_GADGET_DEBUG_FILES -+ -+static int rndis_proc_read (char *page, char **start, off_t off, int count, int *eof, -+ void *data) -+{ -+ char *out = page; -+ int len; -+ rndis_params *param = (rndis_params *) data; -+ -+ out += snprintf (out, count, -+ "Config Nr. %d\n" -+ "used : %s\n" -+ "state : %s\n" -+ "medium : 0x%08X\n" -+ "speed : %d\n" -+ "cable : %s\n" -+ "vendor ID : 0x%08X\n" -+ "vendor : %s\n", -+ param->confignr, (param->used) ? "y" : "n", -+ ({ char *s = "?"; -+ switch (param->state) { -+ case RNDIS_UNINITIALIZED: -+ s = "RNDIS_UNINITIALIZED"; break; -+ case RNDIS_INITIALIZED: -+ s = "RNDIS_INITIALIZED"; break; -+ case RNDIS_DATA_INITIALIZED: -+ s = "RNDIS_DATA_INITIALIZED"; break; -+ }; s; }), -+ param->medium, -+ (param->media_state) ? 0 : param->speed*100, -+ (param->media_state) ? "disconnected" : "connected", -+ param->vendorID, param->vendorDescr); -+ -+ len = out - page; -+ len -= off; -+ -+ if (len < count) { -+ *eof = 1; -+ if (len <= 0) -+ return 0; -+ } else -+ len = count; -+ -+ *start = page + off; -+ return len; -+} -+ -+static int rndis_proc_write (struct file *file, const char __user *buffer, -+ unsigned long count, void *data) -+{ -+ rndis_params *p = data; -+ u32 speed = 0; -+ int i, fl_speed = 0; -+ -+ for (i = 0; i < count; i++) { -+ char c; -+ if (get_user(c, buffer)) -+ return -EFAULT; -+ switch (c) { -+ case '0': -+ case '1': -+ case '2': -+ case '3': -+ case '4': -+ case '5': -+ case '6': -+ case '7': -+ case '8': -+ case '9': -+ fl_speed = 1; -+ speed = speed*10 + c - '0'; -+ break; -+ case 'C': -+ case 'c': -+ rndis_signal_connect (p->confignr); -+ break; -+ case 'D': -+ case 'd': -+ rndis_signal_disconnect(p->confignr); -+ break; -+ default: -+ if (fl_speed) p->speed = speed; -+ else DEBUG ("%c is not valid\n", c); -+ break; -+ } -+ -+ buffer++; -+ } -+ -+ return count; -+} -+ -+#define NAME_TEMPLATE "driver/rndis-%03d" -+ -+static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS]; -+ -+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */ -+ -+ -+int __init rndis_init (void) -+{ -+ u8 i; -+ -+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -+#ifdef CONFIG_USB_GADGET_DEBUG_FILES -+ char name [20]; -+ -+ sprintf (name, NAME_TEMPLATE, i); -+ if (!(rndis_connect_state [i] -+ = create_proc_entry (name, 0660, NULL))) -+ { -+ DEBUG ("%s :remove entries", __FUNCTION__); -+ while (i) { -+ sprintf (name, NAME_TEMPLATE, --i); -+ remove_proc_entry (name, NULL); -+ } -+ DEBUG ("\n"); -+ return -EIO; -+ } -+ -+ rndis_connect_state [i]->nlink = 1; -+ rndis_connect_state [i]->write_proc = rndis_proc_write; -+ rndis_connect_state [i]->read_proc = rndis_proc_read; -+ rndis_connect_state [i]->data = (void *) -+ (rndis_per_dev_params + i); -+#endif -+ rndis_per_dev_params [i].confignr = i; -+ rndis_per_dev_params [i].used = 0; -+ rndis_per_dev_params [i].state = RNDIS_UNINITIALIZED; -+ rndis_per_dev_params [i].media_state -+ = NDIS_MEDIA_STATE_DISCONNECTED; -+ INIT_LIST_HEAD (&(rndis_per_dev_params [i].resp_queue)); -+ } -+ -+ return 0; -+} -+ -+void rndis_exit (void) -+{ -+#ifdef CONFIG_USB_GADGET_DEBUG_FILES -+ u8 i; -+ char name [20]; -+ -+ for (i = 0; i < RNDIS_MAX_CONFIGS; i++) { -+ sprintf (name, NAME_TEMPLATE, i); -+ remove_proc_entry (name, NULL); -+ } -+#endif -+} -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/rndis.h kernel/drivers/usb/gadget/rndis.h ---- /tmp/kernel/drivers/usb/gadget/rndis.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/rndis.h 2005-04-22 17:53:19.504528119 +0200 -@@ -0,0 +1,348 @@ -+/* -+ * RNDIS Definitions for Remote NDIS -+ * -+ * Version: $Id: rndis.h,v 1.15 2004/03/25 21:33:46 robert Exp $ -+ * -+ * Authors: Benedikt Spranger, Pengutronix -+ * Robert Schwebel, Pengutronix -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * version 2, as published by the Free Software Foundation. -+ * -+ * This software was originally developed in conformance with -+ * Microsoft's Remote NDIS Specification License Agreement. -+ */ -+ -+#ifndef _LINUX_RNDIS_H -+#define _LINUX_RNDIS_H -+ -+#include "ndis.h" -+ -+#define RNDIS_MAXIMUM_FRAME_SIZE 1518 -+#define RNDIS_MAX_TOTAL_SIZE 1558 -+ -+/* Remote NDIS Versions */ -+#define RNDIS_MAJOR_VERSION 1 -+#define RNDIS_MINOR_VERSION 0 -+ -+/* Status Values */ -+#define RNDIS_STATUS_SUCCESS 0x00000000U /* Success */ -+#define RNDIS_STATUS_FAILURE 0xC0000001U /* Unspecified error */ -+#define RNDIS_STATUS_INVALID_DATA 0xC0010015U /* Invalid data */ -+#define RNDIS_STATUS_NOT_SUPPORTED 0xC00000BBU /* Unsupported request */ -+#define RNDIS_STATUS_MEDIA_CONNECT 0x4001000BU /* Device connected */ -+#define RNDIS_STATUS_MEDIA_DISCONNECT 0x4001000CU /* Device disconnected */ -+/* For all not specified status messages: -+ * RNDIS_STATUS_Xxx -> NDIS_STATUS_Xxx -+ */ -+ -+/* Message Set for Connectionless (802.3) Devices */ -+#define REMOTE_NDIS_INITIALIZE_MSG 0x00000002U /* Initialize device */ -+#define REMOTE_NDIS_HALT_MSG 0x00000003U -+#define REMOTE_NDIS_QUERY_MSG 0x00000004U -+#define REMOTE_NDIS_SET_MSG 0x00000005U -+#define REMOTE_NDIS_RESET_MSG 0x00000006U -+#define REMOTE_NDIS_INDICATE_STATUS_MSG 0x00000007U -+#define REMOTE_NDIS_KEEPALIVE_MSG 0x00000008U -+ -+/* Message completion */ -+#define REMOTE_NDIS_INITIALIZE_CMPLT 0x80000002U -+#define REMOTE_NDIS_QUERY_CMPLT 0x80000004U -+#define REMOTE_NDIS_SET_CMPLT 0x80000005U -+#define REMOTE_NDIS_RESET_CMPLT 0x80000006U -+#define REMOTE_NDIS_KEEPALIVE_CMPLT 0x80000008U -+ -+/* Device Flags */ -+#define RNDIS_DF_CONNECTIONLESS 0x00000001U -+#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002U -+ -+#define RNDIS_MEDIUM_802_3 0x00000000U -+ -+/* from drivers/net/sk98lin/h/skgepnmi.h */ -+#define OID_PNP_CAPABILITIES 0xFD010100 -+#define OID_PNP_SET_POWER 0xFD010101 -+#define OID_PNP_QUERY_POWER 0xFD010102 -+#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103 -+#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104 -+#define OID_PNP_ENABLE_WAKE_UP 0xFD010106 -+ -+ -+/* supported OIDs */ -+static const u32 oid_supported_list [] = -+{ -+ /* the general stuff */ -+ OID_GEN_SUPPORTED_LIST, -+ OID_GEN_HARDWARE_STATUS, -+ OID_GEN_MEDIA_SUPPORTED, -+ OID_GEN_MEDIA_IN_USE, -+ OID_GEN_MAXIMUM_FRAME_SIZE, -+ OID_GEN_LINK_SPEED, -+ OID_GEN_TRANSMIT_BLOCK_SIZE, -+ OID_GEN_RECEIVE_BLOCK_SIZE, -+ OID_GEN_VENDOR_ID, -+ OID_GEN_VENDOR_DESCRIPTION, -+ OID_GEN_VENDOR_DRIVER_VERSION, -+ OID_GEN_CURRENT_PACKET_FILTER, -+ OID_GEN_MAXIMUM_TOTAL_SIZE, -+ OID_GEN_MEDIA_CONNECT_STATUS, -+ OID_GEN_PHYSICAL_MEDIUM, -+#if 0 -+ OID_GEN_RNDIS_CONFIG_PARAMETER, -+#endif -+ -+ /* the statistical stuff */ -+ OID_GEN_XMIT_OK, -+ OID_GEN_RCV_OK, -+ OID_GEN_XMIT_ERROR, -+ OID_GEN_RCV_ERROR, -+ OID_GEN_RCV_NO_BUFFER, -+#ifdef RNDIS_OPTIONAL_STATS -+ OID_GEN_DIRECTED_BYTES_XMIT, -+ OID_GEN_DIRECTED_FRAMES_XMIT, -+ OID_GEN_MULTICAST_BYTES_XMIT, -+ OID_GEN_MULTICAST_FRAMES_XMIT, -+ OID_GEN_BROADCAST_BYTES_XMIT, -+ OID_GEN_BROADCAST_FRAMES_XMIT, -+ OID_GEN_DIRECTED_BYTES_RCV, -+ OID_GEN_DIRECTED_FRAMES_RCV, -+ OID_GEN_MULTICAST_BYTES_RCV, -+ OID_GEN_MULTICAST_FRAMES_RCV, -+ OID_GEN_BROADCAST_BYTES_RCV, -+ OID_GEN_BROADCAST_FRAMES_RCV, -+ OID_GEN_RCV_CRC_ERROR, -+ OID_GEN_TRANSMIT_QUEUE_LENGTH, -+#endif /* RNDIS_OPTIONAL_STATS */ -+ -+ /* mandatory 802.3 */ -+ /* the general stuff */ -+ OID_802_3_PERMANENT_ADDRESS, -+ OID_802_3_CURRENT_ADDRESS, -+ OID_802_3_MULTICAST_LIST, -+ OID_802_3_MAC_OPTIONS, -+ OID_802_3_MAXIMUM_LIST_SIZE, -+ -+ /* the statistical stuff */ -+ OID_802_3_RCV_ERROR_ALIGNMENT, -+ OID_802_3_XMIT_ONE_COLLISION, -+ OID_802_3_XMIT_MORE_COLLISIONS, -+#ifdef RNDIS_OPTIONAL_STATS -+ OID_802_3_XMIT_DEFERRED, -+ OID_802_3_XMIT_MAX_COLLISIONS, -+ OID_802_3_RCV_OVERRUN, -+ OID_802_3_XMIT_UNDERRUN, -+ OID_802_3_XMIT_HEARTBEAT_FAILURE, -+ OID_802_3_XMIT_TIMES_CRS_LOST, -+ OID_802_3_XMIT_LATE_COLLISIONS, -+#endif /* RNDIS_OPTIONAL_STATS */ -+ -+#ifdef RNDIS_PM -+ /* PM and wakeup are mandatory for USB: */ -+ -+ /* power management */ -+ OID_PNP_CAPABILITIES, -+ OID_PNP_QUERY_POWER, -+ OID_PNP_SET_POWER, -+ -+ /* wake up host */ -+ OID_PNP_ENABLE_WAKE_UP, -+ OID_PNP_ADD_WAKE_UP_PATTERN, -+ OID_PNP_REMOVE_WAKE_UP_PATTERN, -+#endif -+}; -+ -+ -+typedef struct rndis_init_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+ u32 MajorVersion; -+ u32 MinorVersion; -+ u32 MaxTransferSize; -+} rndis_init_msg_type; -+ -+typedef struct rndis_init_cmplt_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+ u32 Status; -+ u32 MajorVersion; -+ u32 MinorVersion; -+ u32 DeviceFlags; -+ u32 Medium; -+ u32 MaxPacketsPerTransfer; -+ u32 MaxTransferSize; -+ u32 PacketAlignmentFactor; -+ u32 AFListOffset; -+ u32 AFListSize; -+} rndis_init_cmplt_type; -+ -+typedef struct rndis_halt_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+} rndis_halt_msg_type; -+ -+typedef struct rndis_query_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+ u32 OID; -+ u32 InformationBufferLength; -+ u32 InformationBufferOffset; -+ u32 DeviceVcHandle; -+} rndis_query_msg_type; -+ -+typedef struct rndis_query_cmplt_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+ u32 Status; -+ u32 InformationBufferLength; -+ u32 InformationBufferOffset; -+} rndis_query_cmplt_type; -+ -+typedef struct rndis_set_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+ u32 OID; -+ u32 InformationBufferLength; -+ u32 InformationBufferOffset; -+ u32 DeviceVcHandle; -+} rndis_set_msg_type; -+ -+typedef struct rndis_set_cmplt_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+ u32 Status; -+} rndis_set_cmplt_type; -+ -+typedef struct rndis_reset_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 Reserved; -+} rndis_reset_msg_type; -+ -+typedef struct rndis_reset_cmplt_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 Status; -+ u32 AddressingReset; -+} rndis_reset_cmplt_type; -+ -+typedef struct rndis_indicate_status_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 Status; -+ u32 StatusBufferLength; -+ u32 StatusBufferOffset; -+} rndis_indicate_status_msg_type; -+ -+typedef struct rndis_keepalive_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+} rndis_keepalive_msg_type; -+ -+typedef struct rndis_keepalive_cmplt_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 RequestID; -+ u32 Status; -+} rndis_keepalive_cmplt_type; -+ -+struct rndis_packet_msg_type -+{ -+ u32 MessageType; -+ u32 MessageLength; -+ u32 DataOffset; -+ u32 DataLength; -+ u32 OOBDataOffset; -+ u32 OOBDataLength; -+ u32 NumOOBDataElements; -+ u32 PerPacketInfoOffset; -+ u32 PerPacketInfoLength; -+ u32 VcHandle; -+ u32 Reserved; -+}; -+ -+struct rndis_config_parameter -+{ -+ u32 ParameterNameOffset; -+ u32 ParameterNameLength; -+ u32 ParameterType; -+ u32 ParameterValueOffset; -+ u32 ParameterValueLength; -+}; -+ -+/* implementation specific */ -+enum rndis_state -+{ -+ RNDIS_UNINITIALIZED, -+ RNDIS_INITIALIZED, -+ RNDIS_DATA_INITIALIZED, -+}; -+ -+typedef struct rndis_resp_t -+{ -+ struct list_head list; -+ u8 *buf; -+ u32 length; -+ int send; -+} rndis_resp_t; -+ -+typedef struct rndis_params -+{ -+ u8 confignr; -+ int used; -+ enum rndis_state state; -+ u32 filter; -+ u32 medium; -+ u32 speed; -+ u32 media_state; -+ const u8 *host_mac; -+ struct net_device *dev; -+ struct net_device_stats *stats; -+ u32 vendorID; -+ const char *vendorDescr; -+ int (*ack) (struct net_device *); -+ struct list_head resp_queue; -+} rndis_params; -+ -+/* RNDIS Message parser and other useless functions */ -+int rndis_msg_parser (u8 configNr, u8 *buf); -+int rndis_register (int (*rndis_control_ack) (struct net_device *)); -+void rndis_deregister (int configNr); -+int rndis_set_param_dev (u8 configNr, struct net_device *dev, -+ struct net_device_stats *stats); -+int rndis_set_param_vendor (u8 configNr, u32 vendorID, -+ const char *vendorDescr); -+int rndis_set_param_medium (u8 configNr, u32 medium, u32 speed); -+void rndis_add_hdr (struct sk_buff *skb); -+int rndis_rm_hdr (u8 *buf, u32 *length); -+u8 *rndis_get_next_response (int configNr, u32 *length); -+void rndis_free_response (int configNr, u8 *buf); -+ -+int rndis_signal_connect (int configNr); -+int rndis_signal_disconnect (int configNr); -+int rndis_state (int configNr); -+extern void rndis_set_host_mac (int configNr, const u8 *addr); -+ -+int __init rndis_init (void); -+void rndis_exit (void); -+ -+#endif /* _LINUX_RNDIS_H */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/superh_udc.c kernel/drivers/usb/gadget/superh_udc.c ---- /tmp/kernel/drivers/usb/gadget/superh_udc.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/superh_udc.c 2005-04-22 17:53:19.510527142 +0200 -@@ -0,0 +1,1819 @@ -+/* -+ * Renesas SuperH USB 1.1 device controller (found on SH7705, SH7727...) -+ * -+ * Copyright (C) 2003 Renesas Technology Europe Limited -+ * Copyright (C) 2003 Julian Back (jback@mpc-data.co.uk), MPC Data Limited -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+/* -+ * This is a driver for the USB Device Controller found on Renesas SH -+ * processors. This is a full-speed controller which has four -+ * endpoints in a single fixed configuration. -+ * -+ * Limitations -+ * -+ * Only tested on SH7705. Mostly tested with Mass Storage gadget -+ * using Bulk-Only Transport. It has been tested with Linux 2.4, -+ * Linux 2.6, Windows 2000 and Windows XP hosts. -+ * -+ * DMA is not (yet) implemented. -+ * -+ * Handling of application stalls is tricky. We set a bit to stall an -+ * endpoint. When the host tries to access the ep it gets a stall and -+ * another stall bit is latched by the device. The host clears the -+ * stall with a clear feature but the hardware doesn't inform us, the -+ * latched bit is cleared but not the bit we have set, so the next -+ * time the host accesses the ep it will get another stall and the -+ * latch will be set again unless we have cleared our stall bit. The -+ * solution adopted in this driver is to use a timer to clear the -+ * application stall bit some time after setting the stall. This -+ * seems to work most of the time but is not 100% reliable. Because -+ * of this it is best to avoid USB protocols that require the USB -+ * device to stall the host. Unfortunately USB mass storage does -+ * require the device to stall when it gets unsupported commands, -+ * Linux hosts don't send any of these unsupported commands but -+ * Windows hosts do. -+ * -+ * Another place where the hardware is too clever is in the handling -+ * of setup packets. Many setup packets including SET_INTERFACE and -+ * SET_CONFIGURATION are handled by the hardware without informing the -+ * driver software. But we need to inform the gadget driver of at -+ * least one of these as it uses this to kick of it's data processing. -+ * The solution adopted is that after we have recieved N setup packets -+ * following a bus reset a fake SET_CONFIGURATION is sent to the -+ * gadget. We also have to arrange things so that the reply to the -+ * fake packet is not sent out. -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/kernel.h> -+#include <linux/ioport.h> -+#include <linux/types.h> -+#include <linux/version.h> -+#include <linux/errno.h> -+#include <linux/delay.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+#include <linux/proc_fs.h> -+#include <linux/mm.h> -+ -+#include <asm/atomic.h> -+#include <asm/byteorder.h> -+#include <asm/dma.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/unaligned.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#undef DEBUG -+#undef VERY_NOISY -+ -+#define DRIVER_DESC "SuperH USB Peripheral Controller" -+#define DRIVER_VERSION "alpha (11 November 2003)" -+ -+#ifdef USE_DMA -+#error "DMA not supported" -+#endif -+ -+static const char driver_name [] = "superh_udc"; -+static const char driver_desc [] = DRIVER_DESC; -+ -+static const char ep0name [] = "ep0"; -+static const char *ep_name [] = { -+ ep0name, -+ "ep1out-bulk", -+ "ep2in-bulk", -+ "ep3in-bulk", -+}; -+ -+static struct superh_udc *the_controller; -+ -+#include "superh_udc.h" -+ -+/* High priority interrupts */ -+#define F0_HIGH (EP1_FULL | EP2_TR | EP2_EMPTY ) -+#define F1_HIGH (0) -+ -+/* Low priority interrupts */ -+#define F0_LOW (BRST | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS) -+#define F1_LOW (EP3_TR | EP3_TS | VBUSF) -+ -+/* How long to leave the stall bit set - this value is quite critical -+ * to making stalls work. Unfortunately it doesn't seem possible to -+ * get a value that will work reliably with both fast and slow -+ * machines. -+ */ -+#define STALL_TIME (HZ/75) -+ -+/* Number of endpoints to check in the unstall timer. It should not -+ * be necessary to unstall bulk endpoints using the timer as long as -+ * the gadget code is aware that this device cannot stall properly -+ * (see the file backed storage gadget for an example). But if the -+ * UDC driver stalls ep0 due to a bad SETUP then the timer is still -+ * required otherwise the stall will never get cleared. If it is -+ * necessary to unstall all endpoints using the timer then set this to -+ * 4. -+ */ -+#define EP_TO_UNSTALL 1 -+ -+/* Number of packets to wait for before sending a fake -+ * SET_CONFIGURATION to the gadget driver -+ */ -+#define DEFAULT_SETUP_COUNT 7 -+#define RESET_SETUP_COUNT 2 -+ -+/* How long to wait for the number of packets specified above */ -+#define SETUP_TIME (HZ/10 ) -+ -+static void superh_ep_fifo_flush(struct usb_ep *_ep); -+static void stop_activity(struct superh_udc *dev, struct usb_gadget_driver *driver); -+static int superh_ep_set_halt(struct usb_ep *_ep, int value); -+static void udc_timer(unsigned long _dev); -+static struct superh_request* process_ep_req(struct superh_ep *ep, -+ struct superh_request *req); -+static void done(struct superh_ep *ep, struct superh_request *req, int status); -+ -+/* -+ * IO -+ */ -+ -+static inline void and_b(u8 mask, unsigned long addr) -+{ -+ ctrl_outb(ctrl_inb(addr) & mask, addr); -+} -+ -+ -+static inline void or_b(u8 mask, unsigned long addr) -+{ -+ ctrl_outb(ctrl_inb(addr) | mask, addr); -+} -+ -+ -+static inline void ep0_idle (struct superh_udc *dev) -+{ -+ DBG(DBG_VERY_NOISY, "ep0_idle\n"); -+ dev->ep0state = EP0_IDLE; -+} -+ -+ -+static void init_udc_timer(struct superh_udc *dev) -+{ -+ init_timer(&dev->timer); -+ dev->timer.function = udc_timer; -+ dev->timer.data = (unsigned long) dev; -+ dev->timer.expires = jiffies + STALL_TIME; -+ add_timer(&dev->timer); -+} -+ -+/* Send a fake SET_CONFIGURATION to the gadget to start it up. -+ * Needed because the hardware doesn't let us know when the real packet -+ * has arrived. -+ */ -+static void send_fake_config(struct superh_udc *dev) -+{ -+ struct usb_ctrlrequest r; -+ dev->fake_config = 1; -+ dev->setup_countdown = 0; -+ r.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD -+ | USB_RECIP_DEVICE; -+ r.bRequest = USB_REQ_SET_CONFIGURATION; -+ r.wValue = 1; /* configuration to select */ -+ r.wIndex = 0; -+ r.wLength = 0; -+ if (dev->driver->setup(&dev->gadget, &r) < 0) { -+ DMSG("SET_CONFIGURATION failed.\n"); -+ } -+} -+ -+/* -+ * Timer function. Clears stall from any stalled endpoints as we -+ * don't get informed when the host has sent a clear feature. -+ */ -+static void udc_timer(unsigned long _dev) -+{ -+ struct superh_udc *dev = (void *)_dev; -+ int i; -+ unsigned long flags; -+ -+ local_irq_save(flags); -+ -+ if (atomic_read(&dev->in_interrupt) == 0) { -+ -+ /* Check if a bus reset has been done and we haven't faked a SET_CONFIGURATION */ -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN -+ && dev->setup_countdown > 0 -+ && jiffies - dev->reset_time > SETUP_TIME -+ &&list_empty(&dev->ep[0].queue)) { -+ send_fake_config(dev); -+ } -+ -+ /* Check if any end points are halted and restart them */ -+ for (i = 0; i < EP_TO_UNSTALL; i++) { -+ struct superh_ep *ep = &dev->ep[i]; -+ if (ep->halted) { -+ DBG(DBG_VERBOSE, "unstalling ep %d\n", i); -+ superh_ep_set_halt(&ep->ep, 0); -+ if (likely (!list_empty(&ep->queue))) { -+ struct superh_request *req -+ = list_entry(ep->queue.next, -+ struct superh_request, queue); -+ process_ep_req(ep, req); -+ } -+ } -+ } -+ } -+ -+ init_udc_timer(dev); -+ -+ local_irq_restore(flags); -+} -+ -+/* -+ * done - retire a request; caller blocked irqs -+ */ -+static void done(struct superh_ep *ep, struct superh_request *req, int status) -+{ -+ unsigned stopped = ep->stopped; -+ -+ DBG(DBG_NOISY, "done: %s %p %d\n", ep->ep.name, req, status); -+ -+ list_del_init(&req->queue); -+ -+ if (likely (req->req.status == -EINPROGRESS)) -+ req->req.status = status; -+ else -+ status = req->req.status; -+ -+ if (status && status != -ESHUTDOWN) -+ DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", -+ ep->ep.name, &req->req, status, -+ req->req.actual, req->req.length); -+ -+ /* don't modify queue heads during completion callback */ -+ ep->stopped = 1; -+ req->req.complete(&ep->ep, &req->req); -+ ep->stopped = stopped; -+} -+ -+/* -+ * Enable interrupts for the specified endpoint -+ */ -+static inline void pio_irq_enable(struct superh_ep *ep) -+{ -+ or_b(ep->interrupt_mask, ep->interrupt_reg); -+} -+ -+/* -+ * Disable interrupts for the specified endpoint -+ */ -+static inline void pio_irq_disable(struct superh_ep *ep) -+{ -+ and_b(~ep->interrupt_mask, ep->interrupt_reg); -+} -+ -+/* -+ * nuke - dequeue ALL requests -+ */ -+static void nuke(struct superh_ep *ep, int status) -+{ -+ struct superh_request *req; -+ -+ DBG(DBG_NOISY, "nuke %s %d\n", ep->ep.name, status); -+ -+ /* called with irqs blocked */ -+#ifdef USE_DMA -+ if (ep->dma >= 0 && !ep->stopped) -+ cancel_dma(ep); -+#endif -+ while (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, -+ struct superh_request, -+ queue); -+ done(ep, req, status); -+ } -+ -+ if (ep->desc) -+ pio_irq_disable (ep); -+} -+ -+static inline void clear_ep_state (struct superh_udc *dev) -+{ -+ unsigned i; -+ -+ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint -+ * fifos, and pending transactions mustn't be continued in any case. -+ */ -+ for (i = 1; i < 4; i++) -+ nuke(&dev->ep[i], -ECONNABORTED); -+} -+ -+/* -+ * write a packet to an endpoint data register -+ */ -+static int -+write_packet(u32 epdr, struct superh_request *req, unsigned max) -+{ -+ u8 *buf; -+ unsigned length, count; -+ -+ buf = req->req.buf + req->req.actual; -+ prefetch(buf); -+ -+ /* how big will this packet be? */ -+ length = min(req->req.length - req->req.actual, max); -+ req->req.actual += length; -+ -+ count = length; -+ while (likely(count--)) -+ ctrl_outb(*buf++, epdr); -+ -+ return length; -+} -+ -+static int -+write_ep0_fifo (struct superh_ep *ep, struct superh_request *req) -+{ -+ unsigned count; -+ int is_short; -+ -+ count = write_packet(USBEPDR0I, req, EP0_FIFO_SIZE); -+ ep->dev->stats.write.bytes += count; -+ -+ /* last packet "must be" short (or a zlp) */ -+ is_short = (count != EP0_FIFO_SIZE); -+ -+ DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, -+ req->req.length - req->req.actual, req); -+ -+ ctrl_outb(EP0i_PKTE, USBTRG); -+ -+ if (unlikely (is_short)) { -+ ep->dev->ep0state = EP0_END_XFER; -+ -+ count = req->req.length; -+ done (ep, req, 0); -+ /* -+ * If we have received a specified number of setups -+ * after a bus reset or connect then fake a -+ * SET_CONFIGURATION to the driver (as we don't get -+ * them from the hardware). -+ */ -+ if (ep->dev->setup_countdown >= 0) -+ ep->dev->setup_countdown--; -+ if (ep->dev->setup_countdown == 0) { -+ send_fake_config(ep->dev); -+ } -+ } -+ -+ return is_short; -+} -+ -+/* -+ * handle_ep0_setup -+ * -+ * Handles a SETUP request on EP0 -+ */ -+static void handle_ep0_setup(struct superh_udc* dev) -+{ -+ int i; -+ union { u8 raw [8]; struct usb_ctrlrequest r; } u; -+ -+ for (i = 0; i < 8; i++) { -+ u.raw[i] = ctrl_inb(USBEPDR0S); -+ } -+ -+ /* Send ACK */ -+ ctrl_outb(EP0s_RDFN, USBTRG); -+ -+ le16_to_cpus (&u.r.wValue); -+ le16_to_cpus (&u.r.wIndex); -+ le16_to_cpus (&u.r.wLength); -+ -+ DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", -+ u.r.bRequestType, u.r.bRequest, -+ u.r.wValue, u.r.wIndex, u.r.wLength); -+ -+ if (u.r.bRequestType & USB_DIR_IN) { -+ DBG(DBG_VERY_NOISY, "handle_ep0_setup: EP0_IN_DATA_PHASE\n"); -+ dev->ep0state = EP0_IN_DATA_PHASE; -+ } -+ else { -+ DBG(DBG_VERY_NOISY, "handle_ep0_setup: EP0_OUT_DATA_PHASE\n"); -+ dev->ep0state = EP0_OUT_DATA_PHASE; -+ } -+ -+ i = dev->driver->setup(&dev->gadget, &u.r); -+ if (i < 0) { -+ DMSG("SETUP %02x.%02x v%04x i%04x l%04x failed\n", -+ u.r.bRequestType, u.r.bRequest, -+ u.r.wValue, u.r.wIndex, u.r.wLength); -+ superh_ep_set_halt(&dev->ep[0].ep, 1); -+ } -+} -+ -+/* -+ * write to an IN endpoint fifo, as many packets as possible. -+ * irqs will use this to write the rest later. -+ * caller guarantees at least one packet buffer is ready. -+ */ -+static int -+write_fifo (struct superh_ep *ep, struct superh_request *req) -+{ -+ unsigned max; -+ -+ DBG(DBG_VERY_NOISY, "write_fifo\n"); -+ -+ if ((ep->bEndpointAddress & USB_DIR_IN) != USB_DIR_IN) { -+ DMSG("write_fifo from invalid EP (%s)\n", ep->ep.name); -+ return -EINVAL; -+ } -+ -+ max = ep->desc->wMaxPacketSize; -+ do { -+ unsigned count; -+ int is_last, is_short; -+ -+ count = write_packet(ep->fifo_reg, req, max); -+ -+ /* last packet is usually short (or a zlp) */ -+ if (unlikely (count != max)) -+ is_last = is_short = 1; -+ else { -+ if (likely(req->req.length != req->req.actual) -+ || req->req.zero) -+ is_last = 0; -+ else -+ is_last = 1; -+ /* interrupt/iso maxpacket may not fill the fifo */ -+ is_short = unlikely (max < ep->ep.maxpacket); -+ -+ /* FIXME ep.maxpacket should be the current size, -+ * modified (for periodic endpoints) when the -+ * ep is enabled. do that, re-init as needed, -+ * and change maxpacket refs accordingly. -+ */ -+ } -+ -+ DBG(DBG_VERY_NOISY, "wrote %s %d bytes%s%s %d left %p\n", -+ ep->ep.name, count, -+ is_last ? "/L" : "", is_short ? "/S" : "", -+ req->req.length - req->req.actual, req); -+ -+ /* let loose that packet. maybe try writing another one, -+ * double buffering might work. -+ */ -+ or_b(ep->packet_enable_mask, USBTRG); -+ -+ /* requests complete when all IN data is in the FIFO */ -+ if (is_last) { -+ done (ep, req, 0); -+ if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) { -+ pio_irq_disable (ep); -+ } -+#ifdef USE_DMA -+ /* TODO */ -+ if (unlikely(ep->dma >= 0) && !list_empty(&ep->queue)) { -+ DMSG("%s pio2dma\n", ep->ep.name); -+ req = list_entry(ep->queue.next, -+ struct superh_request, queue); -+ kick_dma(ep,req); -+ return 0; -+ } -+#endif -+ return 1; -+ } -+ /* Only loop if on EP2 as it is double buffered */ -+ } while (ep->bEndpointAddress == (2|USB_DIR_IN) -+ && ctrl_inb(USBIFR0) & EP2_EMPTY); -+ return 0; -+} -+ -+/* -+ * read_ep0_fifo - unload packets from ep0 control-out fifo. caller -+ * should have made sure there's at least one packet ready. -+ * -+ * returns true if the request completed because of short packet or the -+ * request buffer having filled (and maybe overran till end-of-packet). -+ */ -+static int -+read_ep0_fifo(struct superh_ep *ep, struct superh_request *req) -+{ -+ u8 *buf; -+ unsigned bufferspace, count; -+ -+ DBG(DBG_VERY_NOISY, "read_ep0_fifo\n"); -+ -+ if (!ep) { -+ DMSG("read_ep0_fifo invalid ep\n"); -+ return -EINVAL; -+ } -+ -+ if (!req) { -+ DMSG("read_ep0_fifo invalid req\n"); -+ return -EINVAL; -+ } -+ -+ if (ep->desc != 0) { -+ DMSG("read_ep0_fifo from invalid EP (%s)\n", ep->ep.name); -+ return -EINVAL; -+ } -+ -+ /* make sure there's a packet in the FIFO. -+ */ -+ if (likely ((ctrl_inb(USBIFR0) & EP0o_TS) == 0)) { -+ buf = req->req.buf + req->req.actual; -+ bufferspace = req->req.length - req->req.actual; -+ -+ /* read all bytes from this packet */ -+ count = ctrl_inb(USBEPSZ0O); -+ req->req.actual += min (count, bufferspace); -+ DBG(DBG_VERY_NOISY, "read %s %d bytes req %p %d/%d\n", -+ ep->ep.name, count, -+ req, req->req.actual, req->req.length); -+ while (likely (count-- != 0)) { -+ u8 byte = ctrl_inb(USBEPDR0O); -+ -+ if (unlikely (bufferspace == 0)) { -+ /* this happens when the driver's buffer -+ * is smaller than what the host sent. -+ * discard the extra data. -+ */ -+ if (req->req.status != -EOVERFLOW) -+ DMSG("%s overflow %d\n", -+ ep->ep.name, count); -+ req->req.status = -EOVERFLOW; -+ } else { -+ *buf++ = byte; -+ bufferspace--; -+ } -+ } -+ -+ /* Send ACK */ -+ or_b(EP0o_RDFN, USBTRG); -+ -+ /* completion */ -+ if (req->req.actual >= req->req.length) { -+ done (ep, req, 0); -+ ep0_idle(ep->dev); -+ return 1; -+ } -+ } -+ -+ return 0; -+} -+ -+/* -+ * read_fifo - unload packet(s) from the fifo we use for usb OUT -+ * transfers and put them into the request. caller should have made -+ * sure there's at least one packet ready. -+ * -+ * returns true if the request completed because of short packet or the -+ * request buffer having filled (and maybe overran till end-of-packet). -+ */ -+static int -+read_fifo (struct superh_ep *ep, struct superh_request *req) -+{ -+ DBG(DBG_VERY_NOISY, "read_fifo\n"); -+ -+ if ((ep->bEndpointAddress & 0x0f) != 1) { -+ DMSG("read_fifo from invalid EP (%s)\n", ep->ep.name); -+ return -EINVAL; -+ } -+ -+ for (;;) { -+ u8 *buf; -+ unsigned bufferspace, count, is_short; -+ -+ /* make sure there's a packet in the FIFO. -+ */ -+ if (unlikely ((ctrl_inb(USBIFR0) & EP1_FULL) == 0)) -+ break; -+ buf = req->req.buf + req->req.actual; -+ bufferspace = req->req.length - req->req.actual; -+ -+ /* read all bytes from this packet */ -+ count = ctrl_inb(USBEPSZ1); -+ req->req.actual += min (count, bufferspace); -+ is_short = (count < ep->desc->wMaxPacketSize); -+ DBG(DBG_VERY_NOISY, "read %s %d bytes%s req %p %d/%d\n", -+ ep->ep.name, count, -+ is_short ? "/S" : "", -+ req, req->req.actual, req->req.length); -+ while (likely (count-- != 0)) { -+ u8 byte = ctrl_inb(USBEPDR1); -+ -+ if (unlikely (bufferspace == 0)) { -+ /* this happens when the driver's buffer -+ * is smaller than what the host sent. -+ * discard the extra data. -+ */ -+ if (req->req.status != -EOVERFLOW) -+ DMSG("%s overflow %d\n", -+ ep->ep.name, count); -+ req->req.status = -EOVERFLOW; -+ } else { -+ *buf++ = byte; -+ bufferspace--; -+ } -+ } -+ -+ or_b(EP1_RDFN, USBTRG); -+ /* There could now be another packet because of dual buffer */ -+ -+ /* completion */ -+ if (is_short || req->req.actual == req->req.length) { -+ done (ep, req, 0); -+ if (list_empty(&ep->queue)) -+ pio_irq_disable (ep); -+ return 1; -+ } -+ -+ /* finished that packet. the next one may be waiting... */ -+ } -+ return 0; -+} -+ -+/*--------------------------------------------------------------------------*/ -+/* Interrupt Handler(s) -+ */ -+ -+/* -+ * superh_udc_irq_f0 - high priority interrupt handler -+ * this deals with data to & from the bulk pipes -+ */ -+static void superh_udc_irq_f0(int irq, void *_dev, struct pt_regs *regs) -+{ -+ unsigned char f0_status; -+ struct superh_udc *dev = (struct superh_udc*) _dev; -+ struct superh_request *req; -+ struct superh_ep *ep; -+ -+ DBG(DBG_NOISY, "superh_udc_irq_f0 %p\n", dev); -+ -+ atomic_inc(&dev->in_interrupt); -+ -+ dev->stats.irqs++; -+ dev->stats.irq0s++; -+ f0_status = ctrl_inb(USBIFR0); -+ -+ /* Acknowledge interrupts */ -+ ctrl_outb(~(f0_status & F0_HIGH), USBIFR0); -+ -+ if (f0_status & EP1_FULL) { -+ DBG(DBG_NOISY, "superh_udc_irq_f0 %p: EP1 FULL\n", dev); -+ ep = &dev->ep[1]; -+ -+ if (likely (!list_empty(&ep->queue))) -+ req = list_entry(ep->queue.next, -+ struct superh_request, queue); -+ else -+ req = 0; -+ -+ if (req) -+ read_fifo(ep, req); -+ else -+ pio_irq_disable(ep); -+ } -+ -+ if ( f0_status & (EP2_TR | EP2_EMPTY) ) { -+ DBG(DBG_NOISY, "superh_udc_irq_f0 %p: EP2 TR | EP2_EMPTY\n", dev); -+ ep = &dev->ep[2]; -+ -+ if (likely (!list_empty(&ep->queue))) -+ req = list_entry(ep->queue.next, -+ struct superh_request, queue); -+ else -+ req = 0; -+ -+ if (req) { -+ if ((f0_status & EP2_TR) && (f0_status & EP2_EMPTY)) -+ write_fifo(ep, req); -+ else -+ and_b(~EP2_EMPTY, USBIER0); -+ -+ } -+ else { -+ pio_irq_disable(ep); -+ } -+ } -+ -+ atomic_dec(&dev->in_interrupt); -+} -+ -+/** -+ * superh_udc_irq_f1 - low priority interrupt handler -+ * -+ */ -+static void superh_udc_irq_f1(int irq, void *_dev, struct pt_regs *regs) -+{ -+ unsigned char f0_status; -+ unsigned char f1_status; -+ struct superh_udc *dev = (struct superh_udc*) _dev; -+ -+ atomic_inc(&dev->in_interrupt);; -+ -+ dev->stats.irqs++; -+ dev->stats.irq1s++; -+ -+ f0_status = ctrl_inb(USBIFR0); -+ f1_status = ctrl_inb(USBIFR1); -+ -+ /* Acknowledge interrupts */ -+ ctrl_outb(~(f0_status & F0_LOW), USBIFR0); -+ ctrl_outb(~(f1_status & F1_LOW), USBIFR1); -+ -+ /* VBUSF indicates the USB being connected/disconnected */ -+ if (f1_status & VBUSF) { -+ DBG(DBG_VERY_NOISY, "superh_udc_irq_f1[%lx] VBUSF\n", dev->stats.irqs); -+ if (!is_usb_connected) { -+ /* report disconnect just once */ -+ if (dev->gadget.speed != USB_SPEED_UNKNOWN) { -+ DMSG("disconnect %s\n", -+ dev->driver ? dev->driver->driver.name : 0); -+ stop_activity(dev, dev->driver); -+ } -+ } -+ else if (dev->gadget.speed == USB_SPEED_UNKNOWN) { -+ DMSG("connect\n"); -+ dev->setup_countdown = DEFAULT_SETUP_COUNT; -+ } -+ } -+ -+ -+ -+ /* Bus Reset */ -+ if (f0_status & BRST) { -+ int i; -+ DBG(DBG_VERBOSE, "superh_udc_irq_f1[%lx]: BRST bus reset\n", dev->stats.irqs); -+ /* kill any outstanding requests */ -+ for (i = 0; i < 4; i++) { -+ struct superh_ep *ep = &dev->ep[i]; -+ nuke(ep, -ESHUTDOWN); -+ ep->halted = 0; -+ ep->stopped = 0; -+ } -+ -+ /* reset fifo's and stall's */ -+ ctrl_outb( EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR ); -+ ctrl_outb( 0, USBEPSTL ); -+ DMSG("gadget driver '%s', address zero\n", dev->driver->driver.name); -+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) -+ init_udc_timer(dev); -+ dev->gadget.speed = USB_SPEED_FULL; -+ memset(&dev->stats, 0, sizeof dev->stats); -+ if (dev->setup_countdown < 0) -+ dev->setup_countdown = RESET_SETUP_COUNT; -+ dev->reset_time = jiffies; -+ dev->fake_config = 0; -+ ep0_idle(dev); -+ } -+ -+ /* EPOi Transmit Complete - data to host on EP0 ACKed -+ * EP0i Transfer Request - no data in FIFO to send on EP0 -+ * either way we send next data if there is any and the FIFO is not busy -+ * it will interrupt again if we later if we don't send anything. -+ */ -+ if ((f0_status & EP0i_TR || f0_status & EP0i_TS) -+ && (ctrl_inb(USBDASTS) & EP0i_DE) == 0) { -+ struct superh_ep *ep = &dev->ep[0]; -+ struct superh_request *req; -+ DBG(DBG_VERY_NOISY, "superh_udc_irq_f1[%lx]: ep0i TR\n", dev->stats.irqs); -+ if (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, struct superh_request, queue); -+ write_ep0_fifo(ep, req); -+ } -+ or_b(EP0i_PKTE, USBTRG); -+ } -+ -+ /* Setup Command Receive Complete */ -+ if (f0_status & SETUP_TS) { -+ DBG(DBG_NOISY, "superh_udc_irq_f1[%lx]: SETUP TS\n", dev->stats.irqs); -+ or_b( EP0o_CLEAR | EP0i_CLEAR, USBFCLR); -+ handle_ep0_setup(dev); -+ } -+ -+ /* EPOo Receive Complete - EP0 has received data from host */ -+ if (f0_status & EP0o_TS) { -+ struct superh_request *req; -+ struct superh_ep *ep; -+ DBG(DBG_VERY_NOISY, "superh_int_hndlr_f1[%lx]: ep0o TS\n", dev->stats.irqs); -+ ep = &dev->ep[0]; -+ -+ if (likely (!list_empty(&ep->queue))) -+ req = list_entry(ep->queue.next, -+ struct superh_request, queue); -+ else -+ req = 0; -+ -+ if (req) -+ read_ep0_fifo(ep, req); -+ } -+ -+ /* EP3 Transmit Request & Transmit Complete */ -+ if ( f1_status & (EP3_TR | EP3_TS) ) { -+ struct superh_request *req; -+ struct superh_ep *ep; -+ DBG(DBG_VERY_NOISY, "superh_udc_irq_f1[%lx]: EP3 TR | EP3_TS (%x)\n", dev->stats.irqs, f1_status); -+ ep = &dev->ep[3]; -+ -+ if (likely (!list_empty(&ep->queue))) -+ req = list_entry(ep->queue.next, -+ struct superh_request, queue); -+ else -+ req = 0; -+ -+ if (req) { -+ if ((f1_status & EP3_TR) && (ctrl_inb(USBDASTS) & EP3_DE) == 0) -+ write_fifo(ep, req); -+ -+ } -+ else { -+ pio_irq_disable(ep); -+ } -+ } -+ -+ atomic_dec(&dev->in_interrupt);; -+} -+ -+ -+/*--------------------------------------------------------------------------*/ -+ -+/* -+ * endpoint enable/disable -+ * -+ * we need to verify the descriptors used to enable endpoints. since superh -+ * endpoint configurations are fixed, and are pretty much always enabled, -+ * there's not a lot to manage here. -+ * -+ */ -+static int superh_ep_enable (struct usb_ep *_ep, -+ const struct usb_endpoint_descriptor *desc) -+{ -+ struct superh_ep *ep; -+ struct superh_udc *dev; -+ -+ DBG(DBG_NOISY, "superh_ep_enable\n"); -+ -+ ep = container_of (_ep, struct superh_ep, ep); -+ if (!_ep || !desc || ep->desc || _ep->name == ep0name -+ || desc->bDescriptorType != USB_DT_ENDPOINT -+ || ep->bEndpointAddress != desc->bEndpointAddress -+ || ep->ep.maxpacket < desc->wMaxPacketSize) { -+ DMSG("%s, bad ep or descriptor\n", __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ /* xfer types must match, except that interrupt ~= bulk */ -+ if (ep->bmAttributes != desc->bmAttributes -+ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK -+ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { -+ DMSG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); -+ return -EINVAL; -+ } -+ -+#if 0 -+ /* hardware _could_ do smaller, but driver doesn't */ -+ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK -+ && desc->wMaxPacketSize != BULK_FIFO_SIZE) -+ || !desc->wMaxPacketSize) { -+ DMSG("%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); -+ return -ERANGE; -+ } -+#endif -+ -+ dev = ep->dev; -+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { -+ DMSG("%s, bogus device state\n", __FUNCTION__); -+ return -ESHUTDOWN; -+ } -+ -+ ep->desc = desc; -+ ep->dma = -1; -+ ep->stopped = 0; -+ -+ /* flush fifo (mostly for OUT buffers), enable irq */ -+ superh_ep_fifo_flush (_ep); -+ -+ /* ... reset halt state too, if we could ... */ -+ -+#ifdef USE_DMA -+ -+#endif -+ -+ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); -+ return 0; -+} -+ -+static int superh_ep_disable (struct usb_ep *_ep) -+{ -+ struct superh_ep *ep; -+ -+ DBG(DBG_NOISY, "superh_ep_disable\n"); -+ -+ ep = container_of (_ep, struct superh_ep, ep); -+ if (!_ep || !ep->desc) { -+ DMSG("%s, %s not enabled\n", __FUNCTION__, -+ _ep ? ep->ep.name : NULL); -+ return -EINVAL; -+ } -+ nuke (ep, -ESHUTDOWN); -+ -+#ifdef USE_DMA -+ /* TODO */ -+ if (ep->dma >= 0) { -+ *ep->reg_drcmr = 0; -+ pxa_free_dma (ep->dma); -+ ep->dma = -1; -+ } -+#endif -+ -+ /* flush fifo (mostly for IN buffers) */ -+ superh_ep_fifo_flush (_ep); -+ -+ ep->desc = 0; -+ ep->stopped = 1; -+ -+ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); -+ return 0; -+} -+ -+/* for the superh, these can just wrap kmalloc/kfree. gadget drivers -+ * must still pass correctly initialized endpoints, since other controller -+ * drivers may care about how it's currently set up (dma issues etc). -+ */ -+ -+/* -+ * superh_ep_alloc_request - allocate a request data structure -+ */ -+static struct usb_request * -+superh_ep_alloc_request (struct usb_ep *_ep, int gfp_flags) -+{ -+ struct superh_request *req; -+ -+ /* FIXME for bulk out-dma endpoints, preallocate a frame's worth of -+ * (aligned) dma descriptors at the end of the request -+ */ -+ -+ req = kmalloc (sizeof *req, gfp_flags); -+ if (!req) -+ return 0; -+ -+ memset (req, 0, sizeof *req); -+ INIT_LIST_HEAD (&req->queue); -+ DBG(DBG_VERY_NOISY, "superh_ep_alloc_request: %p %d\n", req, list_empty(&req->queue)); -+ -+ return &req->req; -+} -+ -+/* -+ * superh_ep_free_request - deallocate a request data structure -+ */ -+static void -+superh_ep_free_request (struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct superh_request *req; -+ -+ req = container_of (_req, struct superh_request, req); -+ WARN_ON (!list_empty (&req->queue)); -+ kfree(req); -+} -+ -+/* SH cache needs flushing with DMA I/O (it's dma-incoherent), but there's -+ * no device-affinity and the heap works perfectly well for i/o buffers. -+ * TODO: check this -+ */ -+static void * -+superh_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, -+ dma_addr_t *dma, int gfp_flags) -+{ -+ char *retval; -+ -+ retval = kmalloc (bytes, gfp_flags); -+ if (retval) -+ *dma = virt_to_bus (retval); -+ return retval; -+} -+ -+static void -+superh_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, -+ unsigned bytes) -+{ -+ kfree (buf); -+} -+ -+static struct superh_request* -+process_ep_req(struct superh_ep *ep, struct superh_request *req) -+{ -+ struct superh_udc *dev = ep->dev; -+ -+ if (ep->desc == 0 /* ep0 */) { -+ switch (dev->ep0state) { -+ case EP0_IN_DATA_PHASE: -+ DBG(DBG_VERY_NOISY, "superh_ep_queue: EP0_IN_DATA_PHASE\n"); -+ dev->stats.write.ops++; -+ if (write_ep0_fifo(ep, req)) -+ req = 0; -+ break; -+ -+ case EP0_OUT_DATA_PHASE: -+ DBG(DBG_VERY_NOISY, "superh_ep_queue: EP0_OUT_DATA_PHASE\n"); -+ dev->stats.read.ops++; -+ if (read_ep0_fifo(ep, req)) -+ req = 0; -+ break; -+ -+ default: -+ DMSG("ep0 i/o, odd state %d\n", dev->ep0state); -+ return 0; -+ } -+#ifdef USE_DMA -+ /* either start dma or prime pio pump */ -+ } -+ else if (ep->dma >= 0) { -+ kick_dma(ep, req); -+#endif -+ /* can the FIFO can satisfy the request immediately? */ -+ } -+ else if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { -+ if ((ep->desc->bEndpointAddress & 0x0f) == 2 -+ && (ctrl_inb(USBIFR0) & EP2_TR) != 0 -+ && write_fifo(ep, req)) { -+ req = 0; -+ } -+ else if ((ep->desc->bEndpointAddress & 0x0f) == 3 -+ && (ctrl_inb(USBIFR1) & EP3_TR) != 0 -+ && write_fifo(ep, req)) { -+ req = 0; -+ } -+ } -+ -+ if (likely (((req && ep->desc) && ep->dma < 0) || ep->desc == 0)) -+ pio_irq_enable(ep); -+ -+ return req; -+} -+ -+ -+static int -+superh_ep_queue(struct usb_ep *_ep, struct usb_request *_req, int gfp_flags) -+{ -+ struct superh_request *req; -+ struct superh_ep *ep; -+ struct superh_udc *dev; -+ unsigned long flags; -+ -+ req = container_of(_req, struct superh_request, req); -+ ep = container_of(_ep, struct superh_ep, ep); -+ -+ DBG(DBG_VERY_NOISY, "superh_ep_queue\n"); -+ -+ /* If we have just sent a fake configuration request then -+ * this is the reply. We don't want to send it to the host -+ * so just ignore it. -+ */ -+ if (ep->desc == 0 /* ep0 */ && ep->dev->fake_config) { -+ DBG(DBG_NOISY, "Ignoring bogus SET_CONFIGURATION response\n"); -+ done(ep, req, 0); -+ ep->dev->fake_config = 0; -+ return 1; -+ } -+ -+ if (unlikely (!_req || !_req->complete || !_req->buf -+ || !list_empty(&req->queue))) { -+ DMSG("%s, bad params %s, %p, %p, %p, %d\n", __FUNCTION__, -+ ep->ep.name, _req, _req->complete, _req->buf, -+ list_empty(&req->queue)); -+ return -EINVAL; -+ } -+ -+ if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ dev = ep->dev; -+ if (unlikely (!dev->driver -+ || dev->gadget.speed == USB_SPEED_UNKNOWN)) { -+ DMSG("%s, bogus device state\n", __FUNCTION__); -+ return -ESHUTDOWN; -+ } -+ -+#ifdef USE_DMA -+ /* TODO */ -+ if (ep->dma >= 0) { -+ unsigned long start = (unsigned long) _req->buf; -+ -+ clean_dcache_range(start, start + _req->length); -+ /* or for USB_DIR_OUT, invalidate_dcache_range (...) */ -+ } -+#endif -+ -+ DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", -+ _ep->name, _req, _req->length, _req->buf); -+ -+ local_irq_save(flags); -+ -+ _req->status = -EINPROGRESS; -+ _req->actual = 0; -+ -+ /* kickstart this i/o queue? */ -+ if (list_empty(&ep->queue) && !ep->stopped && !ep->halted) { -+ req = process_ep_req(ep, req); -+ } -+ -+ /* pio or dma irq handler advances the queue. */ -+ if (likely (req != 0)) -+ list_add_tail(&req->queue, &ep->queue); -+ -+ local_irq_restore(flags); -+ -+ return 0; -+} -+ -+static int -+superh_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) -+{ -+ struct superh_ep *ep; -+ struct superh_request *req; -+ unsigned long flags; -+ -+ DBG(DBG_NOISY, "superh_ep_dequeue %s\n", _ep->name); -+ -+ ep = container_of(_ep, struct superh_ep, ep); -+ req = container_of(_req, struct superh_request, req); -+ if (!_ep || !_req || ep->ep.name == ep0name) -+ return -EINVAL; -+ -+ local_irq_save(flags); -+#ifdef USE_DMA -+ if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { -+ cancel_dma(ep); -+ done(ep, req, -ECONNRESET); -+ /* restart i/o */ -+ if (!list_empty(&ep->queue)) { -+ req = list_entry(ep->queue.next, -+ struct superh_request, queue); -+ kick_dma(ep, req); -+ } -+ } else -+#endif -+ if (!list_empty(&req->queue)) -+ done(ep, req, -ECONNRESET); -+ else -+ req = 0; -+ local_irq_restore(flags); -+ -+ return req ? 0 : -EOPNOTSUPP; -+} -+ -+/* stall/unstall an endpoint, 0 clears the stall, 1 sets it */ -+static int -+superh_ep_set_halt(struct usb_ep *_ep, int value) -+{ -+ struct superh_ep *ep; -+ unsigned long flags; -+ -+ ep = container_of(_ep, struct superh_ep, ep); -+ if (unlikely (!_ep -+ || (!ep->desc && ep->ep.name != ep0name)) -+ || ep->bmAttributes == USB_ENDPOINT_XFER_ISOC) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return -EINVAL; -+ } -+ -+ if (ep->halted == value) -+ return 0; -+ -+ local_irq_save(flags); -+ -+ if (value == 1 && (ep->bEndpointAddress & USB_DIR_IN) != 0 -+ && ((ctrl_inb(USBDASTS) & ep->data_present_mask) != 0 -+ || !list_empty(&ep->queue))) { -+ local_irq_restore(flags); -+ DBG(DBG_VERBOSE, "Can't %s on %s\n", value ? " halt" : "clear halt", _ep->name); -+ return -EAGAIN; -+ } -+ -+ if (value) { -+ or_b(ep->stall_mask, USBEPSTL); -+ if (!ep->desc) { -+ ep->dev->ep0state = EP0_STALL; -+ } -+ /* disable ep interrupts and set a timer to clear the stall */ -+ pio_irq_disable(ep); -+ mod_timer(&ep->dev->timer, jiffies + STALL_TIME); -+ } -+ else { -+ and_b(~ep->stall_mask, USBEPSTL); -+ } -+ -+ ep->halted = value; -+ -+ local_irq_restore(flags); -+ -+ DBG(DBG_VERBOSE, "%s %s\n", _ep->name, value ? " halt" : "clear halt"); -+ -+ return 0; -+} -+ -+static int superh_ep_fifo_status(struct usb_ep *_ep) -+{ -+ struct superh_ep *ep; -+ -+ DBG(DBG_NOISY, "superh_ep_fifo_status\n"); -+ -+ ep = container_of(_ep, struct superh_ep, ep); -+ if (!_ep) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return -ENODEV; -+ } -+ if ((ep->bEndpointAddress & USB_DIR_IN) != 0) -+ return -EOPNOTSUPP; -+ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN) -+ return 0; -+ else { -+ switch (ep->desc->bEndpointAddress & 0x0f) { -+ case 0: -+ return ctrl_inb(USBEPSZ0O); -+ case 1: -+ return ctrl_inb(USBEPSZ1); -+ } -+ } -+ -+ return 0; -+} -+ -+static void superh_ep_fifo_flush(struct usb_ep *_ep) -+{ -+ struct superh_ep *ep; -+ -+ DBG(DBG_NOISY, "superh_ep_fifo_flush\n"); -+ -+ ep = container_of(_ep, struct superh_ep, ep); -+ if (!_ep || ep->ep.name == ep0name || !list_empty(&ep->queue)) { -+ DMSG("%s, bad ep\n", __FUNCTION__); -+ return; -+ } -+ -+ or_b(ep->clear_mask, USBFCLR); -+} -+ -+static struct usb_ep_ops superh_ep_ops = { -+ .enable = superh_ep_enable, -+ .disable = superh_ep_disable, -+ -+ .alloc_request = superh_ep_alloc_request, -+ .free_request = superh_ep_free_request, -+ -+ .alloc_buffer = superh_ep_alloc_buffer, -+ .free_buffer = superh_ep_free_buffer, -+ -+ .queue = superh_ep_queue, -+ .dequeue = superh_ep_dequeue, -+ -+ .set_halt = superh_ep_set_halt, -+ .fifo_status = superh_ep_fifo_status, -+ .fifo_flush = superh_ep_fifo_flush, -+}; -+ -+/* --------------------------------------------------------------------------- -+ * device-scoped parts of the api to the usb controller hardware -+ * --------------------------------------------------------------------------- -+ */ -+ -+static int superh_udc_get_frame(struct usb_gadget *_gadget) -+{ -+ DBG(DBG_VERY_NOISY, "superh_udc_get_frame\n"); -+ -+ return -EOPNOTSUPP; -+} -+ -+static const struct usb_gadget_ops superh_udc_ops = { -+ .get_frame = superh_udc_get_frame, -+ // no remote wakeup -+ // always selfpowered -+}; -+ -+ -+/* if we're trying to save space, don't bother with this proc file */ -+ -+#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED) -+# define UDC_PROC_FILE -+#endif -+ -+#ifdef UDC_PROC_FILE -+ -+static const char proc_node_name [] = "driver/udc"; -+ -+static int -+udc_proc_read(char *page, char **start, off_t off, int count, -+ int *eof, void *_dev) -+{ -+ char *buf = page; -+ struct superh_udc *dev = _dev; -+ char *next = buf; -+ unsigned size = count; -+ unsigned long flags; -+ int t; -+ int i; -+ -+ local_irq_save(flags); -+ -+ /* basic device status */ -+ t = snprintf(next, size, -+ "%s\n%s version: %s\nGadget driver: %s\nHost %s\n\n", -+ driver_desc, -+ driver_name, DRIVER_VERSION, -+ dev->driver ? dev->driver->driver.name : "(none)", -+ is_usb_connected ? "full speed" : "disconnected"); -+ size -= t; -+ next += t; -+ -+ /* device registers */ -+ t = snprintf(next, size, -+ "ifr0 %02X, ifr1 %02X, isr0 %02X, isr1 %02X, ier0 %02X, ier1 %02X\n", -+ ctrl_inb(USBIFR0), ctrl_inb(USBIFR1), -+ ctrl_inb(USBISR0), ctrl_inb(USBISR1), -+ ctrl_inb(USBIER0), ctrl_inb(USBIER1)); -+ size -= t; -+ next += t; -+ -+ t = snprintf(next, size, -+ "epsz0o %02X, epsz1 %02X, dasts %02X, dmar %02X\n", -+ ctrl_inb(USBEPSZ0O), ctrl_inb(USBEPSZ1), -+ ctrl_inb(USBDASTS), ctrl_inb(USBDMA)); -+ size -= t; -+ next += t; -+ -+ t = snprintf(next, size, -+ "epstl %02X, xvercr %02X\n", -+ ctrl_inb(USBEPSTL), ctrl_inb(USBXVERCR)); -+ size -= t; -+ next += t; -+ -+ if (!is_usb_connected || !dev->driver) -+ goto done; -+ -+ t = snprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu; irq0s %lu; irq1s %lu\n\n", -+ dev->stats.write.bytes, dev->stats.write.ops, -+ dev->stats.read.bytes, dev->stats.read.ops, -+ dev->stats.irq0s, dev->stats.irq1s); -+ size -= t; -+ next += t; -+ -+ /* dump endpoint queues */ -+ for (i = 0; i < 4; i++) { -+ struct superh_ep *ep = &dev->ep [i]; -+ struct superh_request *req; -+ int t; -+ -+ if (i != 0) { -+ const struct usb_endpoint_descriptor *d; -+ -+ d = ep->desc; -+ if (!d) -+ continue; -+ t = snprintf(next, size, -+ "%s max %d %s\n", -+ ep->ep.name, le16_to_cpu (d->wMaxPacketSize), -+ (ep->dma >= 0) ? "dma" : "pio"); -+ -+ } else /* ep0 should only have one transfer queued */ -+ t = snprintf(next, size, "ep0 max 8 pio\n"); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ -+ if (list_empty(&ep->queue)) { -+ t = snprintf(next, size, "\t(nothing queued)\n"); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ continue; -+ } -+ list_for_each_entry(req, &ep->queue, queue) { -+#ifdef USE_DMA -+ if (ep->dma >= 0 && req->queue.prev == &ep->queue) -+ t = snprintf(next, size, -+ "\treq %p len %d/%d " -+ "buf %p (dma%d dcmd %08x)\n", -+ &req->req, req->req.actual, -+ req->req.length, req->req.buf, -+ ep->dma, DCMD(ep->dma) -+ // low 13 bits == bytes-to-go -+ ); -+ else -+#endif -+ t = snprintf(next, size, -+ "\treq %p len %d/%d buf %p\n", -+ &req->req, req->req.actual, -+ req->req.length, req->req.buf); -+ if (t <= 0 || t > size) -+ goto done; -+ size -= t; -+ next += t; -+ } -+ } -+ -+done: -+ local_irq_restore(flags); -+ return count - size; -+} -+ -+#endif /* UDC_PROC_FILE */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * udc_disable - disable USB device controller -+ */ -+static void udc_disable(struct superh_udc *dev) -+{ -+ /* block all irqs */ -+ ctrl_outb( 0, USBIER0); -+ ctrl_outb( 0, USBIER1); -+ -+ /* Disable the USB module */ -+ or_b(0x80, STBCR3); -+ -+ /* Disable the USB clock */ -+ ctrl_outw(0xA500, UCLKCR); -+ -+ ep0_idle (dev); -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+} -+ -+/* -+ * udc_reinit - initialize software state -+ */ -+static void udc_reinit(struct superh_udc *dev) -+{ -+ u32 i; -+ -+ /* device/ep0 records init */ -+ INIT_LIST_HEAD (&dev->gadget.ep_list); -+ dev->gadget.ep0 = &dev->ep[0].ep; -+ INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); -+ dev->ep0state = EP0_IDLE; -+ -+ /* basic endpoint records init */ -+ for (i = 0; i < 4; i++) { -+ struct superh_ep *ep = &dev->ep[i]; -+ -+ ep->ep.name = ep_name[i]; -+ ep->ep.ops = &superh_ep_ops; -+ if (i != 0) -+ list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); -+ -+ ep->dev = dev; -+ ep->desc = 0; -+ ep->stopped = 0; -+ ep->halted = 0; -+ ep->dma = -1; -+ INIT_LIST_HEAD (&ep->queue); -+ -+ /* address may need USB_DIR_IN, attributes likely wrong */ -+ ep->bEndpointAddress = i; -+ ep->bmAttributes = USB_ENDPOINT_XFER_BULK; -+ } -+ -+ /* TODO at least from here on, static initialization -+ * would work just as well and would need less code space -+ */ -+ -+ /* ep0 == control */ -+ dev->ep[ 0].ep.maxpacket = EP0_FIFO_SIZE; -+ dev->ep[ 0].data_present_mask = EP0i_DE; -+ dev->ep[ 0].stall_mask = EP0_STL; -+ dev->ep[ 0].interrupt_mask = EP0o_TS | EP0i_TR | EP0i_TS; -+ dev->ep[ 0].interrupt_reg = USBIER0; -+ dev->ep[ 0].clear_mask = EP0i_CLEAR | EP0o_CLEAR; -+ dev->ep[ 0].fifo_reg = 0; -+ dev->ep[ 0].packet_enable_mask = 0; -+ -+ dev->ep[ 1].ep.maxpacket = BULK_FIFO_SIZE; -+ dev->ep[ 1].bEndpointAddress |= USB_DIR_OUT; -+ dev->ep[ 1].data_present_mask = 0x00; -+ dev->ep[ 1].stall_mask = EP1_STL; -+ dev->ep[ 1].interrupt_mask = EP1_FULL; -+ dev->ep[ 1].interrupt_reg = USBIER0; -+ dev->ep[ 1].clear_mask = EP1_CLEAR; -+ dev->ep[ 1].fifo_reg = 0; -+ dev->ep[ 1].packet_enable_mask = 0; -+ -+ dev->ep[ 2].ep.maxpacket = BULK_FIFO_SIZE; -+ dev->ep[ 2].bEndpointAddress |= USB_DIR_IN; -+ dev->ep[ 2].data_present_mask = EP2_DE; -+ dev->ep[ 2].stall_mask = EP2_STL; -+ dev->ep[ 2].interrupt_mask = EP2_TR | EP2_EMPTY; -+ dev->ep[ 2].interrupt_reg = USBIER0; -+ dev->ep[ 2].clear_mask = EP2_CLEAR; -+ dev->ep[ 2].fifo_reg = USBEPDR2; -+ dev->ep[ 2].packet_enable_mask = EP2_PKTE; -+ -+ dev->ep[ 3].ep.maxpacket = INT_FIFO_SIZE; -+ dev->ep[ 3].bEndpointAddress |= USB_DIR_IN; -+ dev->ep[ 3].data_present_mask = EP3_DE; -+ dev->ep[ 3].stall_mask = EP3_STL; -+ dev->ep[ 3].interrupt_mask = EP3_TR | EP3_TS; -+ dev->ep[ 3].interrupt_reg = USBIER1; -+ dev->ep[ 3].clear_mask = EP3_CLEAR; -+ dev->ep[ 3].fifo_reg = USBEPDR3; -+ dev->ep[ 3].packet_enable_mask = EP3_PKTE; -+} -+ -+/* until it's enabled, this UDC should be completely invisible -+ * to any USB host. -+ */ -+static void udc_enable (struct superh_udc *dev) -+{ -+#if defined(CONFIG_CPU_SUBTYPE_SH7727) -+ // Reset and then Select Function USB1_pwr_en out (USB) c.f. Section 26, Table 26.1 PTE2 -+ and_w(PN_PB2_MSK, PECR); -+ or_w(PN_PB2_OF, PECR); -+ -+ // Reset and then Select Function UCLK c.f. Section 26, Table 26.1, PTD6 -+ and_w(PN_PB6_MSK, PDCR); -+ or_w(PN_PB6_OF, PDCR); -+ -+ // Stop USB module prior to setting clocks c.f. Section 9.2.3 -+ and_b(~MSTP14, STBCR3); -+ or_b(MSTP14, STBCR3); -+ -+ // Select external clock, 1/1 divisor c.f. Section 11.3.1 -+ or_b(USBDIV_11|USBCKS_EC, EXCPGCR); -+ -+ // Start USB c.f. Section 9.2.3 -+ and_b(~MSTP14, STBCR3); -+ -+ // Disable pullup c.f. Section 23.5.19 -+ or_b(PULLUP_E, USBDMA); -+ //and_b(~PULLUP_E, USBDMA); -+ -+ // Set port 1 to function, disabled c.f. Section 22.2.1 -+ or_w(USB_TRANS_TRAN | USB_SEL_FUNC, EXPFC); -+ -+ // Enable pullup c.f. Section 23.5.19a -+ and_b(~PULLUP_E, USBDMA); -+ //or_b(PULLUP_E, USBDMA); -+#elif defined(CONFIG_CPU_SUBTYPE_SH7705) -+ /* Disable the USB module */ -+ or_b(0x80, STBCR3); -+ -+ /* Set the clock to external & enable */ -+ ctrl_outw(0xA5E0, UCLKCR); -+ -+ /* Enable the USB module */ -+ and_b(0x7f, STBCR3); -+ -+ /* Enable USB pins. */ -+ ctrl_outw(0x01FD, PMCR); /* VBUS */ -+ or_b(PULLUP_E, PMDR); -+#endif -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ dev->stats.irqs = 0; -+ dev->stats.irq0s = 0; -+ dev->stats.irq1s = 0; -+ -+ // reset fifo's and stall's -+ or_b( EP3_CLEAR | EP1_CLEAR | EP2_CLEAR | EP0o_CLEAR | EP0i_CLEAR, USBFCLR); -+ or_b(0, USBEPSTL); -+ -+ /* Setup interrupt priority by using the interrupt select registers */ -+ ctrl_outb(F0_LOW, USBISR0); -+ ctrl_outb(F1_LOW, USBISR1); -+ -+ /* Enable some interrupts */ -+ or_b( BRST | SETUP_TS | EP0o_TS | EP0i_TR | EP0i_TS, USBIER0); -+ or_b( VBUSF, USBIER1); -+} -+ -+/* when a driver is successfully registered, it will receive -+ * control requests including set_configuration(), which enables -+ * non-control requests. then usb traffic follows until a -+ * disconnect is reported. then a host may connect again, or -+ * the driver might get unbound. -+ */ -+int usb_gadget_register_driver(struct usb_gadget_driver *driver) -+{ -+ struct superh_udc *dev = the_controller; -+ int retval; -+ -+ if (!driver -+ /*|| driver->speed != USB_SPEED_FULL -+ || !driver->bind -+ || !driver->unbind -+ || !driver->disconnect -+ || !driver->setup*/) -+ return -EINVAL; -+ if (!dev) -+ return -ENODEV; -+ if (dev->driver) -+ return -EBUSY; -+ -+ /* first hook up the driver ... */ -+ dev->driver = driver; -+ -+ retval = driver->bind(&dev->gadget); -+ if (retval) { -+ DMSG("bind to driver %s --> error %d\n", -+ driver->driver.name, retval); -+ dev->driver = 0; -+ return retval; -+ } -+ -+ /* ... then enable host detection and ep0; and we're ready -+ * for set_configuration as well as eventual disconnect. -+ * NOTE: this shouldn't power up until later. -+ */ -+ udc_enable(dev); -+ -+ DMSG("registered gadget driver '%s'\n", driver->driver.name); -+ dump_state(dev); -+ return 0; -+} -+ -+EXPORT_SYMBOL(usb_gadget_register_driver); -+ -+static void -+stop_activity(struct superh_udc *dev, struct usb_gadget_driver *driver) -+{ -+ int i; -+ -+ /* don't disconnect drivers more than once */ -+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) -+ driver = 0; -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ -+ /* prevent new request submissions, kill any outstanding requests */ -+ for (i = 0; i < 4; i++) { -+ struct superh_ep *ep = &dev->ep[i]; -+ -+ ep->stopped = 1; -+ nuke(ep, -ESHUTDOWN); -+ } -+ -+ del_timer_sync(&dev->timer); -+ -+ /* report disconnect; the driver is already quiesced */ -+ if (driver) -+ driver->disconnect(&dev->gadget); -+ -+ /* re-init driver-visible data structures */ -+ udc_reinit(dev); -+} -+ -+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -+{ -+ struct superh_udc *dev = the_controller; -+ -+ if (!dev) -+ return -ENODEV; -+ if (!driver || driver != dev->driver) -+ return -EINVAL; -+ -+ local_irq_disable(); -+ udc_disable(dev); -+ stop_activity(dev, driver); -+ driver->unbind(&dev->gadget); -+ dev->driver = 0; -+ local_irq_enable(); -+ -+ DMSG("unregistered gadget driver '%s'\n", driver->driver.name); -+ dump_state(dev); -+ return 0; -+} -+ -+EXPORT_SYMBOL(usb_gadget_unregister_driver); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40) -+MODULE_DESCRIPTION(driver_desc); -+#endif -+MODULE_AUTHOR("Julian Back"); -+MODULE_LICENSE("GPL"); -+ -+/* -+ * cleanup - free resources allocated during init -+ */ -+static void /*__exit and */ __init cleanup(void) -+{ -+ struct superh_udc *dev = the_controller; -+ -+ if (!dev) -+ return; -+ -+ udc_disable(dev); -+#ifdef UDC_PROC_FILE -+ remove_proc_entry(proc_node_name, NULL); -+#endif -+ usb_gadget_unregister_driver(dev->driver); -+ -+ if (dev->got_irq0) { -+ free_irq(USBF0_IRQ, dev); -+ dev->got_irq0 = 0; -+ } -+ -+ if (dev->got_irq1) { -+ free_irq(USBF1_IRQ, dev); -+ dev->got_irq1 = 0; -+ } -+ -+ the_controller = 0; -+} -+module_exit (cleanup); -+ -+/* -+ * init - allocate resources -+ */ -+static int __init init(void) -+{ -+ static struct superh_udc memory; -+ -+ struct superh_udc *dev; -+ int retval; -+ -+ printk(KERN_DEBUG "%s: version %s\n", driver_name, DRIVER_VERSION); -+ -+ /* initialize data */ -+ dev = &memory; -+ -+ memset(dev, 0, sizeof *dev); -+ dev->gadget.ops = &superh_udc_ops; -+ dev->gadget.name = driver_name; -+ dev->gadget.dev.bus_id = "udc"; -+ dev->gadget.speed = USB_SPEED_UNKNOWN; -+ -+ dev->vbusmn = 0; -+ -+ atomic_set(&dev->in_interrupt, 0); -+ -+ the_controller = dev; -+ udc_disable(dev); -+ udc_reinit(dev); -+ -+ /* irq setup after old hardware state is cleaned up */ -+ retval = request_irq(USBF0_IRQ, superh_udc_irq_f0, -+ 0/*SA_INTERRUPT | SA_SAMPLE_RANDOM*/, -+ driver_name, dev); -+ if (retval != 0) { -+ printk(KERN_ERR "%s: can't get irq %i, err %d\n", -+ driver_name, USBF0_IRQ, retval); -+ goto failed; -+ } -+ dev->got_irq0 = 1; -+ -+ retval = request_irq(USBF1_IRQ, superh_udc_irq_f1, -+ 0/*SA_INTERRUPT | SA_SAMPLE_RANDOM*/, -+ driver_name, dev); -+ if (retval != 0) { -+ printk(KERN_ERR "%s: can't get irq %i, err %d\n", -+ driver_name, USBF1_IRQ, retval); -+ goto failed; -+ } -+ dev->got_irq1 = 1; -+ -+ printk(KERN_INFO "%s, IRQs %d %d\n", driver_desc, -+ USBF0_IRQ, USBF1_IRQ); -+ dump_state(dev); -+ -+ dev->setup_countdown = DEFAULT_SETUP_COUNT; -+ -+#ifdef UDC_PROC_FILE -+ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev); -+#endif -+ -+ return 0; -+ -+failed: -+ cleanup(); -+ return retval; -+} -+module_init (init); -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/superh_udc.h kernel/drivers/usb/gadget/superh_udc.h ---- /tmp/kernel/drivers/usb/gadget/superh_udc.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/superh_udc.h 2005-04-22 17:53:19.513526654 +0200 -@@ -0,0 +1,363 @@ -+/* -+ * Renesas SuperH USB 1.1 device controller (found on SH7705, SH7727...) -+ * -+ * Copyright (C) 2003 Renesas Technology Europe Limited -+ * Copyright (C) 2003 Julian Back (jback@mpc-data.co.uk), MPC Data Limited -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU General Public License as published by -+ * the Free Software Foundation; either version 2 of the License, or -+ * (at your option) any later version. -+ * -+ * This program is 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ */ -+ -+#ifndef __LINUX_USB_GADGET_SUPERH_UDC_H -+#define __LINUX_USB_GADGET_SUPERH_UDC_H -+ -+#include <linux/types.h> -+ -+struct superh_udc; -+ -+struct superh_ep { -+ struct usb_ep ep; -+ struct superh_udc *dev; -+ -+ const struct usb_endpoint_descriptor *desc; -+ struct list_head queue; -+ int dma; -+ -+ u8 bEndpointAddress; -+ u8 bmAttributes; -+ -+ unsigned stopped : 1; -+ unsigned halted : 1; -+ -+ u8 data_present_mask; -+ u8 stall_mask; -+ u8 interrupt_mask; -+ u8 clear_mask; -+ u8 packet_enable_mask; -+ unsigned interrupt_reg; -+ unsigned fifo_reg; -+}; -+ -+struct superh_request { -+ struct usb_request req; -+ struct list_head queue; -+}; -+ -+enum ep0_state { -+ EP0_IDLE, -+ EP0_IN_DATA_PHASE, -+ EP0_OUT_DATA_PHASE, -+ EP0_END_XFER, -+ EP0_STALL, -+}; -+ -+#define EP0_FIFO_SIZE ((unsigned)8) -+#define BULK_FIFO_SIZE ((unsigned)64) -+#define ISO_FIFO_SIZE ((unsigned)0) -+#define INT_FIFO_SIZE ((unsigned)8) -+ -+struct udc_stats { -+ struct ep0stats { -+ unsigned long ops; -+ unsigned long bytes; -+ } read, write; -+ unsigned long irqs; -+ unsigned long irq0s; -+ unsigned long irq1s; -+}; -+ -+struct superh_udc { -+ struct usb_gadget gadget; -+ struct usb_gadget_driver *driver; -+ atomic_t in_interrupt; -+ -+ enum ep0_state ep0state; -+ struct udc_stats stats; -+ unsigned int vbusmn; -+ unsigned long vbusf_time; -+ unsigned got_irq0 : 1, -+ got_irq1 : 1, -+ fake_config: 1; -+ int setup_countdown; -+ unsigned long reset_time; -+ struct timer_list timer; -+ struct superh_ep ep [4]; -+}; -+ -+/* 2.5 changes ... */ -+ -+#ifndef container_of -+#define container_of list_entry -+#endif -+ -+#ifndef WARN_ON -+#define WARN_ON BUG_ON -+#endif -+ -+/* one I/O pin should be used to detect disconnect */ -+#define is_usb_connected ((ctrl_inb(USBIFR1) & VBUSF) != 0) -+ -+/* Register addresses - should really be in include/asm-sh */ -+ -+#ifdef CONFIG_CPU_SUBTYPE_SH7705 -+ -+#define USBEPDR0I 0xA4480000 -+#define USBEPDR0O 0xA4480004 -+#define USBEPDR0S 0xA4480008 -+#define USBEPDR1 0xA448000C -+#define USBEPDR2 0xA4480010 -+#define USBEPDR3 0xA4480014 -+#define USBIFR0 0xA4480018 -+#define USBIFR1 0xA448001C -+#define USBTRG 0xA4480020 -+#define USBFCLR 0xA4480024 -+#define USBEPSZ0O 0xA4480028 -+#define USBDASTS 0xA448002C -+#define USBEPSTL 0xA4480030 -+#define USBIER0 0xA4480034 -+#define USBIER1 0xA4480038 -+#define USBEPSZ1 0xA448003C -+#define USBDMA 0xA4480040 -+#define USBISR0 0xA4480044 -+#define USBISR1 0xA4480048 -+ -+#define USBXVERCR 0xA4480060 -+ -+#define STBCR3 0xA40A0000 -+#define UCLKCR 0xA40A0008 -+ -+#define PMCR 0xA4000118 -+#define PNCR 0xA400011A -+#define PNCR2 0xA405015A -+ -+#define PMDR 0xA4000138 -+ -+#endif -+ -+/* -+ * Standby Control Register (STBCR3) c.f. 9.2.3 -+ */ -+ -+#define MSTP14 0x10 -+ -+/* -+ * EXCPG Control Register (EXCPGCR) c.f. Section 11.3.1 -+ */ -+ -+#define USBDIVS_EL0 0x00 -+#define USBDIVS_EL1 0x01 -+#define USBDIVS_EL2 0x02 -+ -+#define USBCKS_EL1 0x04 -+#define USBCKS_EL2 0x10 -+#define USBCKS_EL3 0x20 -+ -+#define USBDIV_11 0x00 -+#define USBDIV_12 0x01 -+#define USBDIV_13 0x02 -+ -+#define USBCKS_PC 0x00 -+#define USBCKS_IC 0x20 -+#define USBCKS_BC 0x24 -+#define USBCKS_EC 0x30 -+ -+ -+/* -+ * Extra Pin Function Controller (EXPFC) c.f. Section 22.2.1 -+ */ -+ -+#define USB_TRANS_TRAN 0x00 -+#define USB_TRANS_DIG 0x02 -+ -+#define USB_SEL_HOST 0x00 -+#define USB_SEL_FUNC 0x01 -+ -+ -+/* -+ * USBDMA Setting Register (USBDMAR) c.f. Section 23.5.19 -+ */ -+ -+#define EP1_DMAE 0x01 -+#define EP2_DMAE 0x02 -+ -+#if defined(CONFIG_CPU_SUBTYPE_SH7727) -+#define PULLUP_E 0x04 -+#endif -+ -+#if defined(CONFIG_SH_EDOSK7705) -+#define PULLUP_E 0x01 -+#endif -+ -+/* -+ * USB Interrupt Flag Register 0 (USBIFR0) c.f. Section 23.5.7 -+ */ -+ -+#define BRST 0x80 -+#define EP1_FULL 0x40 -+#define EP2_TR 0x20 -+#define EP2_EMPTY 0x10 -+#define SETUP_TS 0x08 -+#define EP0o_TS 0x04 -+#define EP0i_TR 0x02 -+#define EP0i_TS 0x01 -+ -+ -+/* -+ * USB Interrupt Flag Register 1 (USBIFR1) c.f. Section 23.5.8 -+ */ -+ -+#define VBUSMN 0x08 -+#define EP3_TR 0x04 -+#define EP3_TS 0x02 -+#define VBUSF 0x01 -+ -+/* -+ * USB Trigger Register (USBTRG) c.f. Section 23.5.9 -+ */ -+ -+#define EP0i_PKTE 0x01 -+#define EP0o_PKTE 0x02 -+#define EP0o_RDFN 0x02 -+#define EP0s_PKTE 0x04 -+#define EP0s_RDFN 0x04 -+ -+#define EP2_PKTE 0x10 -+#define EP1_PKTE 0x20 -+#define EP1_RDFN 0x20 -+#define EP3_PKTE 0x40 -+ -+ -+/* -+ * USBFIFO Clear Register (USBFCLR) c.f. Section 23.5.10 -+ */ -+ -+#define EP3_CLEAR 0x40 -+#define EP1_CLEAR 0x20 -+#define EP2_CLEAR 0x10 -+#define EP0o_CLEAR 0x02 -+#define EP0i_CLEAR 0x01 -+ -+ -+/* -+ * USBEPSTL Endpoint Stall Register -+ */ -+#define EP3_STL 0x08 -+#define EP2_STL 0x04 -+#define EP1_STL 0x02 -+#define EP0_STL 0x01 -+ -+/* -+ * USBDASTS Data Status Register -+ */ -+#define EP3_DE 0x20 -+#define EP2_DE 0x10 -+#define EP0i_DE 0x01 -+ -+/* -+ * Port Control Registers (PNCR) c.f. Section 26.2 -+ */ -+#define PN_PB0_OF 0x0000 -+#define PN_PB0_PO 0x0001 -+#define PN_PB0_PI_ON 0x0002 -+#define PN_PB0_PI_OFF 0x0003 -+#define PN_PB0_MSK ~0x0003 -+ -+#define PN_PB1_OF 0x0000 -+#define PN_PB1_PO 0x0004 -+#define PN_PB1_PI_ON 0x0008 -+#define PN_PB1_PI_OFF 0x000c -+#define PN_PB1_MSK ~0x000c -+ -+#define PN_PB2_OF 0x0000 -+#define PN_PB2_PO 0x0010 -+#define PN_PB2_PI_ON 0x0020 -+#define PN_PB2_PI_OFF 0x0030 -+#define PN_PB2_MSK ~0x0030 -+ -+#define PN_PB3_OF 0x0000 -+#define PN_PB3_PO 0x0040 -+#define PN_PB3_PI_ON 0x0080 -+#define PN_PB3_PI_OFF 0x00c0 -+#define PN_PB3_MSK ~0x00c0 -+ -+#define PN_PB4_OF 0x0000 -+#define PN_PB4_PO 0x0100 -+#define PN_PB4_PI_ON 0x0200 -+#define PN_PB4_PI_OFF 0x0300 -+#define PN_PB4_MSK ~0x0300 -+ -+#define PN_PB5_OF 0x0000 -+#define PN_PB5_PO 0x0400 -+#define PN_PB5_PI_ON 0x0800 -+#define PN_PB5_PI_OFF 0x0c00 -+#define PN_PB5_MSK ~0x0c00 -+ -+#define PN_PB6_OF 0x0000 -+#define PN_PB6_PO 0x1000 -+#define PN_PB6_PI_ON 0x2000 -+#define PN_PB6_PI_OFF 0x3000 -+#define PN_PB6_MSK ~0x3000 -+ -+#define PN_PB7_OF 0x0000 -+#define PN_PB7_PO 0x4000 -+#define PN_PB7_PI_ON 0x8000 -+#define PN_PB7_PI_OFF 0xc000 -+#define PN_PB7_MSK ~0xc000 -+ -+/* -+ * Debugging support vanishes in non-debug builds. DBG_NORMAL should be -+ * mostly silent during normal use/testing, with no timing side-effects. -+ */ -+#define DBG_NORMAL 1 /* error paths, device state transitions */ -+#define DBG_VERBOSE 2 /* add some success path trace info */ -+#define DBG_NOISY 3 /* ... even more: request level */ -+#define DBG_VERY_NOISY 4 /* ... even more: packet level */ -+ -+#ifdef DEBUG -+ -+#define DMSG(stuff...) printk(KERN_DEBUG "udc: " stuff) -+ -+#if defined(VERY_NOISY) -+# define UDC_DEBUG DBG_VERY_NOISY -+#elif defined(NOISY) -+# define UDC_DEBUG DBG_NOISY -+#elif defined(VERBOSE) -+# define UDC_DEBUG DBG_VERBOSE -+#else -+# define UDC_DEBUG DBG_NORMAL -+#endif -+ -+static void __attribute__ ((__unused__)) -+dump_state(struct superh_udc *dev) -+{ -+ if (!is_usb_connected) -+ return; -+} -+ -+ -+#else -+ -+#define DMSG(stuff...) do{}while(0) -+ -+#define UDC_DEBUG ((unsigned)0) -+ -+#define dump_state(x) do{}while(0) -+ -+#endif -+ -+#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) -+ -+#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) -+ -+#endif /* __LINUX_USB_GADGET_SUPERH_UDC_H */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/usbstring.c kernel/drivers/usb/gadget/usbstring.c ---- /tmp/kernel/drivers/usb/gadget/usbstring.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/usbstring.c 2005-04-22 17:53:19.516526166 +0200 -@@ -0,0 +1,136 @@ -+/* -+ * Copyright (C) 2003 David Brownell -+ * -+ * This program is free software; you can redistribute it and/or modify -+ * it under the terms of the GNU Lesser General Public License as published -+ * by the Free Software Foundation; either version 2.1 of the License, or -+ * (at your option) any later version. -+ */ -+ -+#include <linux/errno.h> -+#include <linux/kernel.h> -+#include <linux/list.h> -+#include <linux/string.h> -+#include <linux/init.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include <asm/byteorder.h> -+#include <asm/unaligned.h> -+ -+ -+static int utf8_to_utf16le(const char *s, u16 *cp, unsigned len) -+{ -+ int count = 0; -+ u8 c; -+ u16 uchar; -+ -+ /* this insists on correct encodings, though not minimal ones. -+ * BUT it currently rejects legit 4-byte UTF-8 code points, -+ * which need surrogate pairs. (Unicode 3.1 can use them.) -+ */ -+ while (len != 0 && (c = (u8) *s++) != 0) { -+ if (unlikely(c & 0x80)) { -+ // 2-byte sequence: -+ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx -+ if ((c & 0xe0) == 0xc0) { -+ uchar = (c & 0x1f) << 6; -+ -+ c = (u8) *s++; -+ if ((c & 0xc0) != 0xc0) -+ goto fail; -+ c &= 0x3f; -+ uchar |= c; -+ -+ // 3-byte sequence (most CJKV characters): -+ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx -+ } else if ((c & 0xf0) == 0xe0) { -+ uchar = (c & 0x0f) << 12; -+ -+ c = (u8) *s++; -+ if ((c & 0xc0) != 0xc0) -+ goto fail; -+ c &= 0x3f; -+ uchar |= c << 6; -+ -+ c = (u8) *s++; -+ if ((c & 0xc0) != 0xc0) -+ goto fail; -+ c &= 0x3f; -+ uchar |= c; -+ -+ /* no bogus surrogates */ -+ if (0xd800 <= uchar && uchar <= 0xdfff) -+ goto fail; -+ -+ // 4-byte sequence (surrogate pairs, currently rare): -+ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx -+ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx -+ // (uuuuu = wwww + 1) -+ // FIXME accept the surrogate code points (only) -+ -+ } else -+ goto fail; -+ } else -+ uchar = c; -+ put_unaligned (cpu_to_le16 (uchar), cp++); -+ count++; -+ len--; -+ } -+ return count; -+fail: -+ return -1; -+} -+ -+ -+/** -+ * usb_gadget_get_string - fill out a string descriptor -+ * @table: of c strings encoded using UTF-8 -+ * @id: string id, from low byte of wValue in get string descriptor -+ * @buf: at least 256 bytes -+ * -+ * Finds the UTF-8 string matching the ID, and converts it into a -+ * string descriptor in utf16-le. -+ * Returns length of descriptor (always even) or negative errno -+ * -+ * If your driver needs stings in multiple languages, you'll probably -+ * "switch (wIndex) { ... }" in your ep0 string descriptor logic, -+ * using this routine after choosing which set of UTF-8 strings to use. -+ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with -+ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 -+ * characters (which are also widely used in C strings). -+ */ -+int -+usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) -+{ -+ struct usb_string *s; -+ int len; -+ -+ /* descriptor 0 has the language id */ -+ if (id == 0) { -+ buf [0] = 4; -+ buf [1] = USB_DT_STRING; -+ buf [2] = (u8) table->language; -+ buf [3] = (u8) (table->language >> 8); -+ return 4; -+ } -+ for (s = table->strings; s && s->s; s++) -+ if (s->id == id) -+ break; -+ -+ /* unrecognized: stall. */ -+ if (!s || !s->s) -+ return -EINVAL; -+ -+ /* string descriptors have length, tag, then UTF16-LE text */ -+ len = min ((size_t) 126, strlen (s->s)); -+ memset (buf + 2, 0, 2 * len); /* zero all the bytes */ -+ len = utf8_to_utf16le(s->s, (u16 *)&buf[2], len); -+ if (len < 0) -+ return -EINVAL; -+ buf [0] = (len + 1) * 2; -+ buf [1] = USB_DT_STRING; -+ return buf [0]; -+} -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/drivers/usb/gadget/zero.c kernel/drivers/usb/gadget/zero.c ---- /tmp/kernel/drivers/usb/gadget/zero.c 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/drivers/usb/gadget/zero.c 2005-04-22 17:53:19.521525352 +0200 -@@ -0,0 +1,1363 @@ -+/* -+ * zero.c -- Gadget Zero, for USB development -+ * -+ * Copyright (C) 2003-2004 David Brownell -+ * All rights reserved. -+ * -+ * Redistribution and use in source and binary forms, with or without -+ * modification, are permitted provided that the following conditions -+ * are met: -+ * 1. Redistributions of source code must retain the above copyright -+ * notice, this list of conditions, and the following disclaimer, -+ * without modification. -+ * 2. Redistributions in binary form must reproduce the above copyright -+ * notice, this list of conditions and the following disclaimer in the -+ * documentation and/or other materials provided with the distribution. -+ * 3. The names of the above-listed copyright holders may not be used -+ * to endorse or promote products derived from this software without -+ * specific prior written permission. -+ * -+ * ALTERNATIVELY, this software may be distributed under the terms of the -+ * GNU General Public License ("GPL") as published by the Free Software -+ * Foundation, either version 2 of that License or (at your option) any -+ * later version. -+ * -+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -+ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, -+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -+ */ -+ -+ -+/* -+ * Gadget Zero only needs two bulk endpoints, and is an example of how you -+ * can write a hardware-agnostic gadget driver running inside a USB device. -+ * -+ * Hardware details are visible (see CONFIG_USB_ZERO_* below) but don't -+ * affect most of the driver. -+ * -+ * Use it with the Linux host/master side "usbtest" driver to get a basic -+ * functional test of your device-side usb stack, or with "usb-skeleton". -+ * -+ * It supports two similar configurations. One sinks whatever the usb host -+ * writes, and in return sources zeroes. The other loops whatever the host -+ * writes back, so the host can read it. Module options include: -+ * -+ * buflen=N default N=4096, buffer size used -+ * qlen=N default N=32, how many buffers in the loopback queue -+ * loopdefault default false, list loopback config first -+ * -+ * Many drivers will only have one configuration, letting them be much -+ * simpler if they also don't support high speed operation (like this -+ * driver does). -+ */ -+ -+#define DEBUG 1 -+// #define VERBOSE -+ -+#include <linux/config.h> -+#include <linux/kernel.h> -+#include <linux/module.h> -+#include <linux/delay.h> -+#include <linux/ioport.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+#include <linux/smp_lock.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/timer.h> -+#include <linux/list.h> -+#include <linux/interrupt.h> -+#include <linux/uts.h> -+#include <linux/version.h> -+ -+#include <asm/byteorder.h> -+#include <asm/io.h> -+#include <asm/irq.h> -+#include <asm/system.h> -+#include <asm/unaligned.h> -+ -+#include <linux/usb_ch9.h> -+#include <linux/usb_gadget.h> -+ -+#include "gadget_chips.h" -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+#define DRIVER_VERSION "St Patrick's Day 2004" -+ -+static const char shortname [] = "zero"; -+static const char longname [] = "Gadget Zero"; -+ -+static const char source_sink [] = "source and sink data"; -+static const char loopback [] = "loop input to output"; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * driver assumes self-powered hardware, and -+ * has no way for users to trigger remote wakeup. -+ * -+ * this version autoconfigures as much as possible, -+ * which is reasonable for most "bulk-only" drivers. -+ */ -+static const char *EP_IN_NAME; /* source */ -+static const char *EP_OUT_NAME; /* sink */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* big enough to hold our biggest descriptor */ -+#define USB_BUFSIZ 256 -+ -+struct zero_dev { -+ spinlock_t lock; -+ struct usb_gadget *gadget; -+ struct usb_request *req; /* for control responses */ -+ -+ /* when configured, we have one of two configs: -+ * - source data (in to host) and sink it (out from host) -+ * - or loop it back (out from host back in to host) -+ */ -+ u8 config; -+ struct usb_ep *in_ep, *out_ep; -+ -+ /* autoresume timer */ -+ struct timer_list resume; -+}; -+ -+#define xprintk(d,level,fmt,args...) \ -+ printk(level "%s %s: " fmt , shortname , (d)->gadget->dev.bus_id , \ -+ ## args) -+ -+#ifdef DEBUG -+#define DBG(dev,fmt,args...) \ -+ xprintk(dev , KERN_DEBUG , fmt , ## args) -+#else -+#define DBG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* DEBUG */ -+ -+#ifdef VERBOSE -+#define VDBG DBG -+#else -+#define VDBG(dev,fmt,args...) \ -+ do { } while (0) -+#endif /* VERBOSE */ -+ -+#define ERROR(dev,fmt,args...) \ -+ xprintk(dev , KERN_ERR , fmt , ## args) -+#define WARN(dev,fmt,args...) \ -+ xprintk(dev , KERN_WARNING , fmt , ## args) -+#define INFO(dev,fmt,args...) \ -+ xprintk(dev , KERN_INFO , fmt , ## args) -+ -+/*-------------------------------------------------------------------------*/ -+ -+static unsigned buflen = 4096; -+static unsigned qlen = 32; -+static unsigned pattern = 0; -+ -+/* -+ * Normally the "loopback" configuration is second (index 1) so -+ * it's not the default. Here's where to change that order, to -+ * work better with hosts where config changes are problematic. -+ * Or controllers (like superh) that only support one config. -+ */ -+static int loopdefault = 0; -+ -+ -+MODULE_PARM (buflen, "i"); -+MODULE_PARM_DESC (buflen, "size of i/o buffers"); -+ -+MODULE_PARM (qlen, "i"); -+MODULE_PARM_DESC (qlen, "depth of loopback buffering"); -+ -+MODULE_PARM (pattern, "i"); -+MODULE_PARM_DESC (pattern, "0 for default all-zeroes, 1 for mod63"); -+ -+MODULE_PARM (loopdefault, "b"); -+MODULE_PARM_DESC (loopdefault, "true to have default config be loopback"); -+ -+/* -+ * if it's nonzero, autoresume says how many seconds to wait -+ * before trying to wake up the host after suspend. -+ */ -+static unsigned autoresume = 0; -+MODULE_PARM (autoresume, "i"); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* Thanks to NetChip Technologies for donating this product ID. -+ * -+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! -+ * Instead: allocate your own, using normal USB-IF procedures. -+ */ -+#ifndef CONFIG_USB_ZERO_HNPTEST -+#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ -+#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */ -+#else -+#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */ -+#define DRIVER_PRODUCT_NUM 0xbadd -+#endif -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * DESCRIPTORS ... most are static, but strings and (full) -+ * configuration descriptors are built on demand. -+ */ -+ -+#define STRING_MANUFACTURER 25 -+#define STRING_PRODUCT 42 -+#define STRING_SERIAL 101 -+#define STRING_SOURCE_SINK 250 -+#define STRING_LOOPBACK 251 -+ -+/* -+ * This device advertises two configurations; these numbers work -+ * on a pxa250 as well as more flexible hardware. -+ */ -+#define CONFIG_SOURCE_SINK 3 -+#define CONFIG_LOOPBACK 2 -+ -+static struct usb_device_descriptor -+device_desc = { -+ .bLength = sizeof device_desc, -+ .bDescriptorType = USB_DT_DEVICE, -+ -+ .bcdUSB = __constant_cpu_to_le16 (0x0200), -+ .bDeviceClass = USB_CLASS_VENDOR_SPEC, -+ -+ .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM), -+ .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM), -+ .iManufacturer = STRING_MANUFACTURER, -+ .iProduct = STRING_PRODUCT, -+ .iSerialNumber = STRING_SERIAL, -+ .bNumConfigurations = 2, -+}; -+ -+static struct usb_config_descriptor -+source_sink_config = { -+ .bLength = sizeof source_sink_config, -+ .bDescriptorType = USB_DT_CONFIG, -+ -+ /* compute wTotalLength on the fly */ -+ .bNumInterfaces = 1, -+ .bConfigurationValue = CONFIG_SOURCE_SINK, -+ .iConfiguration = STRING_SOURCE_SINK, -+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -+ .bMaxPower = 1, /* self-powered */ -+}; -+ -+static struct usb_config_descriptor -+loopback_config = { -+ .bLength = sizeof loopback_config, -+ .bDescriptorType = USB_DT_CONFIG, -+ -+ /* compute wTotalLength on the fly */ -+ .bNumInterfaces = 1, -+ .bConfigurationValue = CONFIG_LOOPBACK, -+ .iConfiguration = STRING_LOOPBACK, -+ .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, -+ .bMaxPower = 1, /* self-powered */ -+}; -+ -+static struct usb_otg_descriptor -+otg_descriptor = { -+ .bLength = sizeof otg_descriptor, -+ .bDescriptorType = USB_DT_OTG, -+ -+ .bmAttributes = USB_OTG_SRP, -+}; -+ -+/* one interface in each configuration */ -+ -+static const struct usb_interface_descriptor -+source_sink_intf = { -+ .bLength = sizeof source_sink_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bNumEndpoints = 2, -+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, -+ .iInterface = STRING_SOURCE_SINK, -+}; -+ -+static const struct usb_interface_descriptor -+loopback_intf = { -+ .bLength = sizeof loopback_intf, -+ .bDescriptorType = USB_DT_INTERFACE, -+ -+ .bNumEndpoints = 2, -+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC, -+ .iInterface = STRING_LOOPBACK, -+}; -+ -+/* two full speed bulk endpoints; their use is config-dependent */ -+ -+static struct usb_endpoint_descriptor -+fs_source_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_IN, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+}; -+ -+static struct usb_endpoint_descriptor -+fs_sink_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bEndpointAddress = USB_DIR_OUT, -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+}; -+ -+static const struct usb_descriptor_header *fs_source_sink_function [] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+ (struct usb_descriptor_header *) &source_sink_intf, -+ (struct usb_descriptor_header *) &fs_sink_desc, -+ (struct usb_descriptor_header *) &fs_source_desc, -+ NULL, -+}; -+ -+static const struct usb_descriptor_header *fs_loopback_function [] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+ (struct usb_descriptor_header *) &loopback_intf, -+ (struct usb_descriptor_header *) &fs_sink_desc, -+ (struct usb_descriptor_header *) &fs_source_desc, -+ NULL, -+}; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ -+/* -+ * usb 2.0 devices need to expose both high speed and full speed -+ * descriptors, unless they only run at full speed. -+ * -+ * that means alternate endpoint descriptors (bigger packets) -+ * and a "device qualifier" ... plus more construction options -+ * for the config descriptor. -+ */ -+ -+static struct usb_endpoint_descriptor -+hs_source_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16 (512), -+}; -+ -+static struct usb_endpoint_descriptor -+hs_sink_desc = { -+ .bLength = USB_DT_ENDPOINT_SIZE, -+ .bDescriptorType = USB_DT_ENDPOINT, -+ -+ .bmAttributes = USB_ENDPOINT_XFER_BULK, -+ .wMaxPacketSize = __constant_cpu_to_le16 (512), -+}; -+ -+static struct usb_qualifier_descriptor -+dev_qualifier = { -+ .bLength = sizeof dev_qualifier, -+ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, -+ -+ .bcdUSB = __constant_cpu_to_le16 (0x0200), -+ .bDeviceClass = USB_CLASS_VENDOR_SPEC, -+ -+ .bNumConfigurations = 2, -+}; -+ -+static const struct usb_descriptor_header *hs_source_sink_function [] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+ (struct usb_descriptor_header *) &source_sink_intf, -+ (struct usb_descriptor_header *) &hs_source_desc, -+ (struct usb_descriptor_header *) &hs_sink_desc, -+ NULL, -+}; -+ -+static const struct usb_descriptor_header *hs_loopback_function [] = { -+ (struct usb_descriptor_header *) &otg_descriptor, -+ (struct usb_descriptor_header *) &loopback_intf, -+ (struct usb_descriptor_header *) &hs_source_desc, -+ (struct usb_descriptor_header *) &hs_sink_desc, -+ NULL, -+}; -+ -+/* maxpacket and other transfer characteristics vary by speed. */ -+#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs)) -+ -+#else -+ -+/* if there's no high speed support, maxpacket doesn't change. */ -+#define ep_desc(g,hs,fs) fs -+ -+#endif /* !CONFIG_USB_GADGET_DUALSPEED */ -+ -+static char manufacturer [50]; -+static char serial [40]; -+ -+/* static strings, in UTF-8 */ -+static struct usb_string strings [] = { -+ { STRING_MANUFACTURER, manufacturer, }, -+ { STRING_PRODUCT, longname, }, -+ { STRING_SERIAL, serial, }, -+ { STRING_LOOPBACK, loopback, }, -+ { STRING_SOURCE_SINK, source_sink, }, -+ { } /* end of list */ -+}; -+ -+static struct usb_gadget_strings stringtab = { -+ .language = 0x0409, /* en-us */ -+ .strings = strings, -+}; -+ -+/* -+ * config descriptors are also handcrafted. these must agree with code -+ * that sets configurations, and with code managing interfaces and their -+ * altsettings. other complexity may come from: -+ * -+ * - high speed support, including "other speed config" rules -+ * - multiple configurations -+ * - interfaces with alternate settings -+ * - embedded class or vendor-specific descriptors -+ * -+ * this handles high speed, and has a second config that could as easily -+ * have been an alternate interface setting (on most hardware). -+ * -+ * NOTE: to demonstrate (and test) more USB capabilities, this driver -+ * should include an altsetting to test interrupt transfers, including -+ * high bandwidth modes at high speed. (Maybe work like Intel's test -+ * device?) -+ */ -+static int -+config_buf (struct usb_gadget *gadget, -+ u8 *buf, u8 type, unsigned index) -+{ -+ int is_source_sink; -+ int len; -+ const struct usb_descriptor_header **function; -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ int hs = (gadget->speed == USB_SPEED_HIGH); -+#endif -+ -+ /* two configurations will always be index 0 and index 1 */ -+ if (index > 1) -+ return -EINVAL; -+ is_source_sink = loopdefault ? (index == 1) : (index == 0); -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ if (type == USB_DT_OTHER_SPEED_CONFIG) -+ hs = !hs; -+ if (hs) -+ function = is_source_sink -+ ? hs_source_sink_function -+ : hs_loopback_function; -+ else -+#endif -+ function = is_source_sink -+ ? fs_source_sink_function -+ : fs_loopback_function; -+ -+ /* for now, don't advertise srp-only devices */ -+ if (!gadget->is_otg) -+ function++; -+ -+ len = usb_gadget_config_buf (is_source_sink -+ ? &source_sink_config -+ : &loopback_config, -+ buf, USB_BUFSIZ, function); -+ if (len < 0) -+ return len; -+ ((struct usb_config_descriptor *) buf)->bDescriptorType = type; -+ return len; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_request * -+alloc_ep_req (struct usb_ep *ep, unsigned length) -+{ -+ struct usb_request *req; -+ -+ req = usb_ep_alloc_request (ep, GFP_ATOMIC); -+ if (req) { -+ req->length = length; -+ req->buf = usb_ep_alloc_buffer (ep, length, -+ &req->dma, GFP_ATOMIC); -+ if (!req->buf) { -+ usb_ep_free_request (ep, req); -+ req = NULL; -+ } -+ } -+ return req; -+} -+ -+static void free_ep_req (struct usb_ep *ep, struct usb_request *req) -+{ -+ if (req->buf) -+ usb_ep_free_buffer (ep, req->buf, req->dma, req->length); -+ usb_ep_free_request (ep, req); -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* optionally require specific source/sink data patterns */ -+ -+static int -+check_read_data ( -+ struct zero_dev *dev, -+ struct usb_ep *ep, -+ struct usb_request *req -+) -+{ -+ unsigned i; -+ u8 *buf = req->buf; -+ -+ for (i = 0; i < req->actual; i++, buf++) { -+ switch (pattern) { -+ /* all-zeroes has no synchronization issues */ -+ case 0: -+ if (*buf == 0) -+ continue; -+ break; -+ /* mod63 stays in sync with short-terminated transfers, -+ * or otherwise when host and gadget agree on how large -+ * each usb transfer request should be. resync is done -+ * with set_interface or set_config. -+ */ -+ case 1: -+ if (*buf == (u8)(i % 63)) -+ continue; -+ break; -+ } -+ ERROR (dev, "bad OUT byte, buf [%d] = %d\n", i, *buf); -+ usb_ep_set_halt (ep); -+ return -EINVAL; -+ } -+ return 0; -+} -+ -+static void -+reinit_write_data ( -+ struct zero_dev *dev, -+ struct usb_ep *ep, -+ struct usb_request *req -+) -+{ -+ unsigned i; -+ u8 *buf = req->buf; -+ -+ switch (pattern) { -+ case 0: -+ memset (req->buf, 0, req->length); -+ break; -+ case 1: -+ for (i = 0; i < req->length; i++) -+ *buf++ = (u8) (i % 63); -+ break; -+ } -+} -+ -+/* if there is only one request in the queue, there'll always be an -+ * irq delay between end of one request and start of the next. -+ * that prevents using hardware dma queues. -+ */ -+static void source_sink_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct zero_dev *dev = ep->driver_data; -+ int status = req->status; -+ -+ switch (status) { -+ -+ case 0: /* normal completion? */ -+ if (ep == dev->out_ep) -+ check_read_data (dev, ep, req); -+ else -+ reinit_write_data (dev, ep, req); -+ break; -+ -+ /* this endpoint is normally active while we're configured */ -+ case -ECONNABORTED: /* hardware forced ep reset */ -+ case -ECONNRESET: /* request dequeued */ -+ case -ESHUTDOWN: /* disconnect from host */ -+ VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status, -+ req->actual, req->length); -+ if (ep == dev->out_ep) -+ check_read_data (dev, ep, req); -+ free_ep_req (ep, req); -+ return; -+ -+ case -EOVERFLOW: /* buffer overrun on read means that -+ * we didn't provide a big enough -+ * buffer. -+ */ -+ default: -+#if 1 -+ DBG (dev, "%s complete --> %d, %d/%d\n", ep->name, -+ status, req->actual, req->length); -+#endif -+ case -EREMOTEIO: /* short read */ -+ break; -+ } -+ -+ status = usb_ep_queue (ep, req, GFP_ATOMIC); -+ if (status) { -+ ERROR (dev, "kill %s: resubmit %d bytes --> %d\n", -+ ep->name, req->length, status); -+ usb_ep_set_halt (ep); -+ /* FIXME recover later ... somehow */ -+ } -+} -+ -+static struct usb_request * -+source_sink_start_ep (struct usb_ep *ep, int gfp_flags) -+{ -+ struct usb_request *req; -+ int status; -+ -+ req = alloc_ep_req (ep, buflen); -+ if (!req) -+ return NULL; -+ -+ memset (req->buf, 0, req->length); -+ req->complete = source_sink_complete; -+ -+ if (strcmp (ep->name, EP_IN_NAME) == 0) -+ reinit_write_data (ep->driver_data, ep, req); -+ -+ status = usb_ep_queue (ep, req, gfp_flags); -+ if (status) { -+ struct zero_dev *dev = ep->driver_data; -+ -+ ERROR (dev, "start %s --> %d\n", ep->name, status); -+ free_ep_req (ep, req); -+ req = NULL; -+ } -+ -+ return req; -+} -+ -+static int -+set_source_sink_config (struct zero_dev *dev, int gfp_flags) -+{ -+ int result = 0; -+ struct usb_ep *ep; -+ struct usb_gadget *gadget = dev->gadget; -+ -+ gadget_for_each_ep (ep, gadget) { -+ const struct usb_endpoint_descriptor *d; -+ -+ /* one endpoint writes (sources) zeroes in (to the host) */ -+ if (strcmp (ep->name, EP_IN_NAME) == 0) { -+ d = ep_desc (gadget, &hs_source_desc, &fs_source_desc); -+ result = usb_ep_enable (ep, d); -+ if (result == 0) { -+ ep->driver_data = dev; -+ if (source_sink_start_ep (ep, gfp_flags) != 0) { -+ dev->in_ep = ep; -+ continue; -+ } -+ usb_ep_disable (ep); -+ result = -EIO; -+ } -+ -+ /* one endpoint reads (sinks) anything out (from the host) */ -+ } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { -+ d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc); -+ result = usb_ep_enable (ep, d); -+ if (result == 0) { -+ ep->driver_data = dev; -+ if (source_sink_start_ep (ep, gfp_flags) != 0) { -+ dev->out_ep = ep; -+ continue; -+ } -+ usb_ep_disable (ep); -+ result = -EIO; -+ } -+ -+ /* ignore any other endpoints */ -+ } else -+ continue; -+ -+ /* stop on error */ -+ ERROR (dev, "can't start %s, result %d\n", ep->name, result); -+ break; -+ } -+ if (result == 0) -+ DBG (dev, "buflen %d\n", buflen); -+ -+ /* caller is responsible for cleanup on error */ -+ return result; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void loopback_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ struct zero_dev *dev = ep->driver_data; -+ int status = req->status; -+ -+ switch (status) { -+ -+ case 0: /* normal completion? */ -+ if (ep == dev->out_ep) { -+ /* loop this OUT packet back IN to the host */ -+ req->zero = (req->actual < req->length); -+ req->length = req->actual; -+ status = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC); -+ if (status == 0) -+ return; -+ -+ /* "should never get here" */ -+ ERROR (dev, "can't loop %s to %s: %d\n", -+ ep->name, dev->in_ep->name, -+ status); -+ } -+ -+ /* queue the buffer for some later OUT packet */ -+ req->length = buflen; -+ status = usb_ep_queue (dev->out_ep, req, GFP_ATOMIC); -+ if (status == 0) -+ return; -+ -+ /* "should never get here" */ -+ /* FALLTHROUGH */ -+ -+ default: -+ ERROR (dev, "%s loop complete --> %d, %d/%d\n", ep->name, -+ status, req->actual, req->length); -+ /* FALLTHROUGH */ -+ -+ /* NOTE: since this driver doesn't maintain an explicit record -+ * of requests it submitted (just maintains qlen count), we -+ * rely on the hardware driver to clean up on disconnect or -+ * endpoint disable. -+ */ -+ case -ECONNABORTED: /* hardware forced ep reset */ -+ case -ECONNRESET: /* request dequeued */ -+ case -ESHUTDOWN: /* disconnect from host */ -+ free_ep_req (ep, req); -+ return; -+ } -+} -+ -+static int -+set_loopback_config (struct zero_dev *dev, int gfp_flags) -+{ -+ int result = 0; -+ struct usb_ep *ep; -+ struct usb_gadget *gadget = dev->gadget; -+ -+ gadget_for_each_ep (ep, gadget) { -+ const struct usb_endpoint_descriptor *d; -+ -+ /* one endpoint writes data back IN to the host */ -+ if (strcmp (ep->name, EP_IN_NAME) == 0) { -+ d = ep_desc (gadget, &hs_source_desc, &fs_source_desc); -+ result = usb_ep_enable (ep, d); -+ if (result == 0) { -+ ep->driver_data = dev; -+ dev->in_ep = ep; -+ continue; -+ } -+ -+ /* one endpoint just reads OUT packets */ -+ } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { -+ d = ep_desc (gadget, &hs_sink_desc, &fs_sink_desc); -+ result = usb_ep_enable (ep, d); -+ if (result == 0) { -+ ep->driver_data = dev; -+ dev->out_ep = ep; -+ continue; -+ } -+ -+ /* ignore any other endpoints */ -+ } else -+ continue; -+ -+ /* stop on error */ -+ ERROR (dev, "can't enable %s, result %d\n", ep->name, result); -+ break; -+ } -+ -+ /* allocate a bunch of read buffers and queue them all at once. -+ * we buffer at most 'qlen' transfers; fewer if any need more -+ * than 'buflen' bytes each. -+ */ -+ if (result == 0) { -+ struct usb_request *req; -+ unsigned i; -+ -+ ep = dev->out_ep; -+ for (i = 0; i < qlen && result == 0; i++) { -+ req = alloc_ep_req (ep, buflen); -+ if (req) { -+ req->complete = loopback_complete; -+ result = usb_ep_queue (ep, req, GFP_ATOMIC); -+ if (result) -+ DBG (dev, "%s queue req --> %d\n", -+ ep->name, result); -+ } else -+ result = -ENOMEM; -+ } -+ } -+ if (result == 0) -+ DBG (dev, "qlen %d, buflen %d\n", qlen, buflen); -+ -+ /* caller is responsible for cleanup on error */ -+ return result; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void zero_reset_config (struct zero_dev *dev) -+{ -+ if (dev->config == 0) -+ return; -+ -+ DBG (dev, "reset config\n"); -+ -+ /* just disable endpoints, forcing completion of pending i/o. -+ * all our completion handlers free their requests in this case. -+ */ -+ if (dev->in_ep) { -+ usb_ep_disable (dev->in_ep); -+ dev->in_ep = NULL; -+ } -+ if (dev->out_ep) { -+ usb_ep_disable (dev->out_ep); -+ dev->out_ep = NULL; -+ } -+ dev->config = 0; -+ del_timer (&dev->resume); -+} -+ -+/* change our operational config. this code must agree with the code -+ * that returns config descriptors, and altsetting code. -+ * -+ * it's also responsible for power management interactions. some -+ * configurations might not work with our current power sources. -+ * -+ * note that some device controller hardware will constrain what this -+ * code can do, perhaps by disallowing more than one configuration or -+ * by limiting configuration choices (like the pxa2xx). -+ */ -+static int -+zero_set_config (struct zero_dev *dev, unsigned number, int gfp_flags) -+{ -+ int result = 0; -+ struct usb_gadget *gadget = dev->gadget; -+ -+ if (number == dev->config) -+ return 0; -+ -+ if (gadget_is_sa1100 (gadget) && dev->config) { -+ /* tx fifo is full, but we can't clear it...*/ -+ INFO (dev, "can't change configurations\n"); -+ return -ESPIPE; -+ } -+ zero_reset_config (dev); -+ -+ switch (number) { -+ case CONFIG_SOURCE_SINK: -+ result = set_source_sink_config (dev, gfp_flags); -+ break; -+ case CONFIG_LOOPBACK: -+ result = set_loopback_config (dev, gfp_flags); -+ break; -+ default: -+ result = -EINVAL; -+ /* FALL THROUGH */ -+ case 0: -+ return result; -+ } -+ -+ if (!result && (!dev->in_ep || !dev->out_ep)) -+ result = -ENODEV; -+ if (result) -+ zero_reset_config (dev); -+ else { -+ char *speed; -+ -+ switch (gadget->speed) { -+ case USB_SPEED_LOW: speed = "low"; break; -+ case USB_SPEED_FULL: speed = "full"; break; -+ case USB_SPEED_HIGH: speed = "high"; break; -+ default: speed = "?"; break; -+ } -+ -+ dev->config = number; -+ INFO (dev, "%s speed config #%d: %s\n", speed, number, -+ (number == CONFIG_SOURCE_SINK) -+ ? source_sink : loopback); -+ } -+ return result; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req) -+{ -+ if (req->status || req->actual != req->length) -+ DBG ((struct zero_dev *) ep->driver_data, -+ "setup complete --> %d, %d/%d\n", -+ req->status, req->actual, req->length); -+} -+ -+/* -+ * The setup() callback implements all the ep0 functionality that's -+ * not handled lower down, in hardware or the hardware driver (like -+ * device and endpoint feature flags, and their status). It's all -+ * housekeeping for the gadget function we're implementing. Most of -+ * the work is in config-specific setup. -+ */ -+static int -+zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) -+{ -+ struct zero_dev *dev = get_gadget_data (gadget); -+ struct usb_request *req = dev->req; -+ int value = -EOPNOTSUPP; -+ -+ /* usually this stores reply data in the pre-allocated ep0 buffer, -+ * but config change events will reconfigure hardware. -+ */ -+ req->zero = 0; -+ switch (ctrl->bRequest) { -+ -+ case USB_REQ_GET_DESCRIPTOR: -+ if (ctrl->bRequestType != USB_DIR_IN) -+ goto unknown; -+ switch (ctrl->wValue >> 8) { -+ -+ case USB_DT_DEVICE: -+ value = min (ctrl->wLength, (u16) sizeof device_desc); -+ memcpy (req->buf, &device_desc, value); -+ break; -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ case USB_DT_DEVICE_QUALIFIER: -+ if (!gadget->is_dualspeed) -+ break; -+ value = min (ctrl->wLength, (u16) sizeof dev_qualifier); -+ memcpy (req->buf, &dev_qualifier, value); -+ break; -+ -+ case USB_DT_OTHER_SPEED_CONFIG: -+ if (!gadget->is_dualspeed) -+ break; -+ // FALLTHROUGH -+#endif /* CONFIG_USB_GADGET_DUALSPEED */ -+ case USB_DT_CONFIG: -+ value = config_buf (gadget, req->buf, -+ ctrl->wValue >> 8, -+ ctrl->wValue & 0xff); -+ if (value >= 0) -+ value = min (ctrl->wLength, (u16) value); -+ break; -+ -+ case USB_DT_STRING: -+ /* wIndex == language code. -+ * this driver only handles one language, you can -+ * add string tables for other languages, using -+ * any UTF-8 characters -+ */ -+ value = usb_gadget_get_string (&stringtab, -+ ctrl->wValue & 0xff, req->buf); -+ if (value >= 0) -+ value = min (ctrl->wLength, (u16) value); -+ break; -+ } -+ break; -+ -+ /* currently two configs, two speeds */ -+ case USB_REQ_SET_CONFIGURATION: -+ if (ctrl->bRequestType != 0) -+ goto unknown; -+ if (gadget->a_hnp_support) -+ DBG (dev, "HNP available\n"); -+ else if (gadget->a_alt_hnp_support) -+ DBG (dev, "HNP needs a different root port\n"); -+ else -+ VDBG (dev, "HNP inactive\n"); -+ spin_lock (&dev->lock); -+ value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC); -+ spin_unlock (&dev->lock); -+ break; -+ case USB_REQ_GET_CONFIGURATION: -+ if (ctrl->bRequestType != USB_DIR_IN) -+ goto unknown; -+ *(u8 *)req->buf = dev->config; -+ value = min (ctrl->wLength, (u16) 1); -+ break; -+ -+ /* until we add altsetting support, or other interfaces, -+ * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) -+ * and already killed pending endpoint I/O. -+ */ -+ case USB_REQ_SET_INTERFACE: -+ if (ctrl->bRequestType != USB_RECIP_INTERFACE) -+ goto unknown; -+ spin_lock (&dev->lock); -+ if (dev->config && ctrl->wIndex == 0 && ctrl->wValue == 0) { -+ u8 config = dev->config; -+ -+ /* resets interface configuration, forgets about -+ * previous transaction state (queued bufs, etc) -+ * and re-inits endpoint state (toggle etc) -+ * no response queued, just zero status == success. -+ * if we had more than one interface we couldn't -+ * use this "reset the config" shortcut. -+ */ -+ zero_reset_config (dev); -+ zero_set_config (dev, config, GFP_ATOMIC); -+ value = 0; -+ } -+ spin_unlock (&dev->lock); -+ break; -+ case USB_REQ_GET_INTERFACE: -+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) -+ goto unknown; -+ if (!dev->config) -+ break; -+ if (ctrl->wIndex != 0) { -+ value = -EDOM; -+ break; -+ } -+ *(u8 *)req->buf = 0; -+ value = min (ctrl->wLength, (u16) 1); -+ break; -+ -+ /* -+ * These are the same vendor-specific requests supported by -+ * Intel's USB 2.0 compliance test devices. We exceed that -+ * device spec by allowing multiple-packet requests. -+ */ -+ case 0x5b: /* control WRITE test -- fill the buffer */ -+ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) -+ goto unknown; -+ if (ctrl->wValue || ctrl->wIndex) -+ break; -+ /* just read that many bytes into the buffer */ -+ if (ctrl->wLength > USB_BUFSIZ) -+ break; -+ value = ctrl->wLength; -+ break; -+ case 0x5c: /* control READ test -- return the buffer */ -+ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) -+ goto unknown; -+ if (ctrl->wValue || ctrl->wIndex) -+ break; -+ /* expect those bytes are still in the buffer; send back */ -+ if (ctrl->wLength > USB_BUFSIZ -+ || ctrl->wLength != req->length) -+ break; -+ value = ctrl->wLength; -+ break; -+ -+ default: -+unknown: -+ VDBG (dev, -+ "unknown control req%02x.%02x v%04x i%04x l%d\n", -+ ctrl->bRequestType, ctrl->bRequest, -+ ctrl->wValue, ctrl->wIndex, ctrl->wLength); -+ } -+ -+ /* respond with data transfer before status phase? */ -+ if (value >= 0) { -+ req->length = value; -+ req->zero = value < ctrl->wLength -+ && (value % gadget->ep0->maxpacket) == 0; -+ value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); -+ if (value < 0) { -+ DBG (dev, "ep_queue --> %d\n", value); -+ req->status = 0; -+ zero_setup_complete (gadget->ep0, req); -+ } -+ } -+ -+ /* device either stalls (value < 0) or reports success */ -+ return value; -+} -+ -+static void -+zero_disconnect (struct usb_gadget *gadget) -+{ -+ struct zero_dev *dev = get_gadget_data (gadget); -+ unsigned long flags; -+ -+ spin_lock_irqsave (&dev->lock, flags); -+ zero_reset_config (dev); -+ -+ /* a more significant application might have some non-usb -+ * activities to quiesce here, saving resources like power -+ * or pushing the notification up a network stack. -+ */ -+ spin_unlock_irqrestore (&dev->lock, flags); -+ -+ /* next we may get setup() calls to enumerate new connections; -+ * or an unbind() during shutdown (including removing module). -+ */ -+} -+ -+static void -+zero_autoresume (unsigned long _dev) -+{ -+ struct zero_dev *dev = (struct zero_dev *) _dev; -+ int status; -+ -+ /* normally the host would be woken up for something -+ * more significant than just a timer firing... -+ */ -+ if (dev->gadget->speed != USB_SPEED_UNKNOWN) { -+ status = usb_gadget_wakeup (dev->gadget); -+ DBG (dev, "wakeup --> %d\n", status); -+ } -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void -+zero_unbind (struct usb_gadget *gadget) -+{ -+ struct zero_dev *dev = get_gadget_data (gadget); -+ -+ DBG (dev, "unbind\n"); -+ -+ /* we've already been disconnected ... no i/o is active */ -+ if (dev->req) -+ free_ep_req (gadget->ep0, dev->req); -+ del_timer_sync (&dev->resume); -+ kfree (dev); -+ set_gadget_data (gadget, NULL); -+} -+ -+static int -+zero_bind (struct usb_gadget *gadget) -+{ -+ struct zero_dev *dev; -+ struct usb_ep *ep; -+ -+ /* Bulk-only drivers like this one SHOULD be able to -+ * autoconfigure on any sane usb controller driver, -+ * but there may also be important quirks to address. -+ */ -+ usb_ep_autoconfig_reset (gadget); -+ ep = usb_ep_autoconfig (gadget, &fs_source_desc); -+ if (!ep) { -+autoconf_fail: -+ printk (KERN_ERR "%s: can't autoconfigure on %s\n", -+ shortname, gadget->name); -+ return -ENODEV; -+ } -+ EP_IN_NAME = ep->name; -+ ep->driver_data = ep; /* claim */ -+ -+ ep = usb_ep_autoconfig (gadget, &fs_sink_desc); -+ if (!ep) -+ goto autoconf_fail; -+ EP_OUT_NAME = ep->name; -+ ep->driver_data = ep; /* claim */ -+ -+ -+ /* -+ * DRIVER POLICY CHOICE: you may want to do this differently. -+ * One thing to avoid is reusing a bcdDevice revision code -+ * with different host-visible configurations or behavior -+ * restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc -+ */ -+ if (gadget_is_net2280 (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201); -+ } else if (gadget_is_pxa (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0203); -+#if 0 -+ } else if (gadget_is_sh(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0204); -+ /* SH has only one configuration; see "loopdefault" */ -+ device_desc.bNumConfigurations = 1; -+ /* FIXME make 1 == default.bConfigurationValue */ -+#endif -+ } else if (gadget_is_sa1100 (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0205); -+ } else if (gadget_is_goku (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0206); -+ } else if (gadget_is_mq11xx (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0207); -+ } else if (gadget_is_omap (gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0208); -+ } else if (gadget_is_lh7a40x(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0209); -+ } else if (gadget_is_n9604(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210); -+ } else if (gadget_is_pxa27x(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211); -+ } else if (gadget_is_s3c2410(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212); -+ } else if (gadget_is_at91(gadget)) { -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x0213); -+ } else { -+ /* gadget zero is so simple (for now, no altsettings) that -+ * it SHOULD NOT have problems with bulk-capable hardware. -+ * so warn about unrcognized controllers, don't panic. -+ * -+ * things like configuration and altsetting numbering -+ * can need hardware-specific attention though. -+ */ -+ printk (KERN_WARNING "%s: controller '%s' not recognized\n", -+ shortname, gadget->name); -+ device_desc.bcdDevice = __constant_cpu_to_le16 (0x9999); -+ } -+ -+ -+ /* ok, we made sense of the hardware ... */ -+ dev = kmalloc (sizeof *dev, SLAB_KERNEL); -+ if (!dev) -+ return -ENOMEM; -+ memset (dev, 0, sizeof *dev); -+ spin_lock_init (&dev->lock); -+ dev->gadget = gadget; -+ set_gadget_data (gadget, dev); -+ -+ /* preallocate control response and buffer */ -+ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); -+ if (!dev->req) -+ goto enomem; -+ dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, -+ &dev->req->dma, GFP_KERNEL); -+ if (!dev->req->buf) -+ goto enomem; -+ -+ dev->req->complete = zero_setup_complete; -+ -+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; -+ -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ /* assume ep0 uses the same value for both speeds ... */ -+ dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; -+ -+ /* and that all endpoints are dual-speed */ -+ hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; -+ hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; -+#endif -+ -+ if (gadget->is_otg) { -+ otg_descriptor.bmAttributes |= USB_OTG_HNP, -+ source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ } -+ -+ if (gadget->is_otg) { -+ otg_descriptor.bmAttributes |= USB_OTG_HNP, -+ source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ } -+ -+ usb_gadget_set_selfpowered (gadget); -+ -+ init_timer (&dev->resume); -+ dev->resume.function = zero_autoresume; -+ dev->resume.data = (unsigned long) dev; -+ if (autoresume) { -+ source_sink_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ loopback_config.bmAttributes |= USB_CONFIG_ATT_WAKEUP; -+ } -+ -+ gadget->ep0->driver_data = dev; -+ -+ INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname); -+ INFO (dev, "using %s, OUT %s IN %s\n", gadget->name, -+ EP_OUT_NAME, EP_IN_NAME); -+ -+ snprintf (manufacturer, sizeof manufacturer, -+ UTS_SYSNAME " " UTS_RELEASE " with %s", -+ gadget->name); -+ -+ return 0; -+ -+enomem: -+ zero_unbind (gadget); -+ return -ENOMEM; -+} -+ -+/*-------------------------------------------------------------------------*/ -+ -+static void -+zero_suspend (struct usb_gadget *gadget) -+{ -+ struct zero_dev *dev = get_gadget_data (gadget); -+ -+ if (gadget->speed == USB_SPEED_UNKNOWN) -+ return; -+ -+ if (autoresume) { -+ mod_timer (&dev->resume, jiffies + (HZ * autoresume)); -+ DBG (dev, "suspend, wakeup in %d seconds\n", autoresume); -+ } else -+ DBG (dev, "suspend\n"); -+} -+ -+static void -+zero_resume (struct usb_gadget *gadget) -+{ -+ struct zero_dev *dev = get_gadget_data (gadget); -+ -+ DBG (dev, "resume\n"); -+ del_timer (&dev->resume); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+static struct usb_gadget_driver zero_driver = { -+#ifdef CONFIG_USB_GADGET_DUALSPEED -+ .speed = USB_SPEED_HIGH, -+#else -+ .speed = USB_SPEED_FULL, -+#endif -+ .function = (char *) longname, -+ .bind = zero_bind, -+ .unbind = zero_unbind, -+ -+ .setup = zero_setup, -+ .disconnect = zero_disconnect, -+ -+ .suspend = zero_suspend, -+ .resume = zero_resume, -+ -+ .driver = { -+ .name = (char *) shortname, -+ // .shutdown = ... -+ // .suspend = ... -+ // .resume = ... -+ }, -+}; -+ -+MODULE_AUTHOR ("David Brownell"); -+MODULE_LICENSE ("Dual BSD/GPL"); -+ -+ -+static int __init init (void) -+{ -+ /* a real value would likely come through some id prom -+ * or module option. this one takes at least two packets. -+ */ -+ strncpy (serial, "0123456789.0123456789.0123456789", sizeof serial); -+ serial [sizeof serial - 1] = 0; -+ -+ return usb_gadget_register_driver (&zero_driver); -+} -+module_init (init); -+ -+static void __exit cleanup (void) -+{ -+ usb_gadget_unregister_driver (&zero_driver); -+} -+module_exit (cleanup); -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/include/linux/moduleparam.h kernel/include/linux/moduleparam.h ---- /tmp/kernel/include/linux/moduleparam.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/include/linux/moduleparam.h 2005-04-22 17:53:19.357552052 +0200 -@@ -0,0 +1,25 @@ -+#ifndef _LINUX_MODULE_PARAMS_H -+#define _LINUX_MODULE_PARAMS_H -+/* Macros for (very simple) module parameter compatibility with 2.6. */ -+#include <linux/module.h> -+ -+/* type is byte, short, ushort, int, uint, long, ulong, bool. (2.6 -+ has more, but they are not supported). perm is permissions when -+ it appears in sysfs: 0 means doens't appear, 0444 means read-only -+ by everyone, 0644 means changable dynamically by root, etc. name -+ must be in scope (unlike MODULE_PARM). -+*/ -+#define module_param(name, type, perm) \ -+ static inline void *__check_existence_##name(void) { return &name; } \ -+ MODULE_PARM(name, _MODULE_PARM_STRING_ ## type) -+ -+#define _MODULE_PARM_STRING_byte "b" -+#define _MODULE_PARM_STRING_short "h" -+#define _MODULE_PARM_STRING_ushort "h" -+#define _MODULE_PARM_STRING_int "i" -+#define _MODULE_PARM_STRING_uint "i" -+#define _MODULE_PARM_STRING_long "l" -+#define _MODULE_PARM_STRING_ulong "l" -+#define _MODULE_PARM_STRING_bool "i" -+ -+#endif /* _LINUX_MODULE_PARAM_TYPES_H */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/include/linux/usb_cdc.h kernel/include/linux/usb_cdc.h ---- /tmp/kernel/include/linux/usb_cdc.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/include/linux/usb_cdc.h 2005-04-22 17:53:19.360551563 +0200 -@@ -0,0 +1,204 @@ -+/* -+ * USB Communications Device Class (CDC) definitions -+ * -+ * CDC says how to talk to lots of different types of network adapters, -+ * notably ethernet adapters and various modems. It's used mostly with -+ * firmware based USB peripherals. -+ * -+ * (C) Copyright 2005 by David Brownell -+ * All Rights Reserved. -+ * -+ * This software is licensed under the GNU GPL version 2. -+ */ -+ -+#define USB_CDC_SUBCLASS_ACM 0x02 -+#define USB_CDC_SUBCLASS_ETHERNET 0x06 -+#define USB_CDC_SUBCLASS_WHCM 0x08 -+#define USB_CDC_SUBCLASS_DMM 0x09 -+#define USB_CDC_SUBCLASS_MDLM 0x0a -+#define USB_CDC_SUBCLASS_OBEX 0x0b -+ -+#define USB_CDC_PROTO_NONE 0 -+ -+#define USB_CDC_ACM_PROTO_AT_V25TER 1 -+#define USB_CDC_ACM_PROTO_AT_PCCA101 2 -+#define USB_CDC_ACM_PROTO_AT_PCCA101_WAKE 3 -+#define USB_CDC_ACM_PROTO_AT_GSM 4 -+#define USB_CDC_ACM_PROTO_AT_3G 5 -+#define USB_CDC_ACM_PROTO_AT_CDMA 6 -+#define USB_CDC_ACM_PROTO_VENDOR 0xff -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* 2.6 "sparse" support for checking beyond what GCC does */ -+ -+#define __le16 u16 -+#define __le32 u32 -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Class-Specific descriptors ... there are a couple dozen of them -+ */ -+ -+#define USB_CDC_HEADER_TYPE 0x00 /* header_desc */ -+#define USB_CDC_CALL_MANAGEMENT_TYPE 0x01 /* call_mgmt_descriptor */ -+#define USB_CDC_ACM_TYPE 0x02 /* acm_descriptor */ -+#define USB_CDC_UNION_TYPE 0x06 /* union_desc */ -+#define USB_CDC_COUNTRY_TYPE 0x07 -+#define USB_CDC_ETHERNET_TYPE 0x0f /* ether_desc */ -+#define USB_CDC_WHCM_TYPE 0x11 -+#define USB_CDC_MDLM_TYPE 0x12 /* mdlm_desc */ -+#define USB_CDC_MDLM_DETAIL_TYPE 0x13 /* mdlm_detail_desc */ -+#define USB_CDC_DMM_TYPE 0x14 -+#define USB_CDC_OBEX_TYPE 0x15 -+ -+/* "Header Functional Descriptor" from CDC spec 5.2.3.1 */ -+struct usb_cdc_header_desc { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ __u8 bDescriptorSubType; -+ -+ __le16 bcdCDC; -+} __attribute__ ((packed)); -+ -+/* "Call Management Descriptor" from CDC spec 5.2.3.2 */ -+struct usb_cdc_call_mgmt_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ __u8 bDescriptorSubType; -+ -+ __u8 bmCapabilities; -+#define USB_CDC_CALL_MGMT_CAP_CALL_MGMT 0x01 -+#define USB_CDC_CALL_MGMT_CAP_DATA_INTF 0x02 -+ -+ __u8 bDataInterface; -+} __attribute__ ((packed)); -+ -+/* "Abstract Control Management Descriptor" from CDC spec 5.2.3.3 */ -+struct usb_cdc_acm_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ __u8 bDescriptorSubType; -+ -+ __u8 bmCapabilities; -+} __attribute__ ((packed)); -+ -+/* "Union Functional Descriptor" from CDC spec 5.2.3.8 */ -+struct usb_cdc_union_desc { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ __u8 bDescriptorSubType; -+ -+ __u8 bMasterInterface0; -+ __u8 bSlaveInterface0; -+ /* ... and there could be other slave interfaces */ -+} __attribute__ ((packed)); -+ -+/* "Ethernet Networking Functional Descriptor" from CDC spec 5.2.3.16 */ -+struct usb_cdc_ether_desc { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ __u8 bDescriptorSubType; -+ -+ __u8 iMACAddress; -+ __le32 bmEthernetStatistics; -+ __le16 wMaxSegmentSize; -+ __le16 wNumberMCFilters; -+ __u8 bNumberPowerFilters; -+} __attribute__ ((packed)); -+ -+/* "MDLM Functional Descriptor" from CDC WMC spec 6.7.2.3 */ -+struct usb_cdc_mdlm_desc { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ __u8 bDescriptorSubType; -+ -+ __le16 bcdVersion; -+ __u8 bGUID[16]; -+} __attribute__ ((packed)); -+ -+/* "MDLM Detail Functional Descriptor" from CDC WMC spec 6.7.2.4 */ -+struct usb_cdc_mdlm_detail_desc { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ __u8 bDescriptorSubType; -+ -+ /* type is associated with mdlm_desc.bGUID */ -+ __u8 bGuidDescriptorType; -+ __u8 bDetailData[]; -+} __attribute__ ((packed)); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Class-Specific Control Requests (6.2) -+ * -+ * section 3.6.2.1 table 4 has the ACM profile, for modems. -+ * section 3.8.2 table 10 has the ethernet profile. -+ * -+ * Microsoft's RNDIS stack for Ethernet is a vendor-specific CDC ACM variant, -+ * heavily dependent on the encapsulated (proprietary) command mechanism. -+ */ -+ -+#define USB_CDC_SEND_ENCAPSULATED_COMMAND 0x00 -+#define USB_CDC_GET_ENCAPSULATED_RESPONSE 0x01 -+#define USB_CDC_REQ_SET_LINE_CODING 0x20 -+#define USB_CDC_REQ_GET_LINE_CODING 0x21 -+#define USB_CDC_REQ_SET_CONTROL_LINE_STATE 0x22 -+#define USB_CDC_REQ_SEND_BREAK 0x23 -+#define USB_CDC_SET_ETHERNET_MULTICAST_FILTERS 0x40 -+#define USB_CDC_SET_ETHERNET_PM_PATTERN_FILTER 0x41 -+#define USB_CDC_GET_ETHERNET_PM_PATTERN_FILTER 0x42 -+#define USB_CDC_SET_ETHERNET_PACKET_FILTER 0x43 -+#define USB_CDC_GET_ETHERNET_STATISTIC 0x44 -+ -+/* Line Coding Structure from CDC spec 6.2.13 */ -+struct usb_cdc_line_coding { -+ __le32 dwDTERate; -+ __u8 bCharFormat; -+#define USB_CDC_1_STOP_BITS 0 -+#define USB_CDC_1_5_STOP_BITS 1 -+#define USB_CDC_2_STOP_BITS 2 -+ -+ __u8 bParityType; -+#define USB_CDC_NO_PARITY 0 -+#define USB_CDC_ODD_PARITY 1 -+#define USB_CDC_EVEN_PARITY 2 -+#define USB_CDC_MARK_PARITY 3 -+#define USB_CDC_SPACE_PARITY 4 -+ -+ __u8 bDataBits; -+} __attribute__ ((packed)); -+ -+/* table 62; bits in multicast filter */ -+#define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) -+#define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ -+#define USB_CDC_PACKET_TYPE_DIRECTED (1 << 2) -+#define USB_CDC_PACKET_TYPE_BROADCAST (1 << 3) -+#define USB_CDC_PACKET_TYPE_MULTICAST (1 << 4) /* filtered */ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * Class-Specific Notifications (6.3) sent by interrupt transfers -+ * -+ * section 3.8.2 table 11 of the CDC spec lists Ethernet notifications -+ * section 3.6.2.1 table 5 specifies ACM notifications, accepted by RNDIS -+ * RNDIS also defines its own bit-incompatible notifications -+ */ -+ -+#define USB_CDC_NOTIFY_NETWORK_CONNECTION 0x00 -+#define USB_CDC_NOTIFY_RESPONSE_AVAILABLE 0x01 -+#define USB_CDC_NOTIFY_SERIAL_STATE 0x20 -+#define USB_CDC_NOTIFY_SPEED_CHANGE 0x2a -+ -+struct usb_cdc_notification { -+ __u8 bmRequestType; -+ __u8 bNotificationType; -+ __le16 wValue; -+ __le16 wIndex; -+ __le16 wLength; -+} __attribute__ ((packed)); -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/include/linux/usb_ch9.h kernel/include/linux/usb_ch9.h ---- /tmp/kernel/include/linux/usb_ch9.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/include/linux/usb_ch9.h 2005-04-22 17:53:19.363551075 +0200 -@@ -0,0 +1,384 @@ -+/* -+ * This file holds USB constants and structures that are needed for USB -+ * device APIs. These are used by the USB device model, which is defined -+ * in chapter 9 of the USB 2.0 specification. Linux has several APIs in C -+ * that need these: -+ * -+ * - the master/host side Linux-USB kernel driver API; -+ * - the "usbfs" user space API; and -+ * - (eventually) a Linux "gadget" slave/device side driver API. -+ * -+ * USB 2.0 adds an additional "On The Go" (OTG) mode, which lets systems -+ * act either as a USB master/host or as a USB slave/device. That means -+ * the master and slave side APIs will benefit from working well together. -+ */ -+ -+#ifndef __LINUX_USB_CH9_H -+#define __LINUX_USB_CH9_H -+ -+#include <asm/types.h> /* __u8 etc */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* CONTROL REQUEST SUPPORT */ -+ -+/* -+ * USB directions -+ * -+ * This bit flag is used in endpoint descriptors' bEndpointAddress field. -+ * It's also one of three fields in control requests bRequestType. -+ */ -+#define USB_DIR_OUT 0 /* to device */ -+#define USB_DIR_IN 0x80 /* to host */ -+ -+/* -+ * USB types, the second of three bRequestType fields -+ */ -+#define USB_TYPE_MASK (0x03 << 5) -+#define USB_TYPE_STANDARD (0x00 << 5) -+#define USB_TYPE_CLASS (0x01 << 5) -+#define USB_TYPE_VENDOR (0x02 << 5) -+#define USB_TYPE_RESERVED (0x03 << 5) -+ -+/* -+ * USB recipients, the third of three bRequestType fields -+ */ -+#define USB_RECIP_MASK 0x1f -+#define USB_RECIP_DEVICE 0x00 -+#define USB_RECIP_INTERFACE 0x01 -+#define USB_RECIP_ENDPOINT 0x02 -+#define USB_RECIP_OTHER 0x03 -+ -+/* -+ * Standard requests, for the bRequest field of a SETUP packet. -+ * -+ * These are qualified by the bRequestType field, so that for example -+ * TYPE_CLASS or TYPE_VENDOR specific feature flags could be retrieved -+ * by a GET_STATUS request. -+ */ -+#define USB_REQ_GET_STATUS 0x00 -+#define USB_REQ_CLEAR_FEATURE 0x01 -+#define USB_REQ_SET_FEATURE 0x03 -+#define USB_REQ_SET_ADDRESS 0x05 -+#define USB_REQ_GET_DESCRIPTOR 0x06 -+#define USB_REQ_SET_DESCRIPTOR 0x07 -+#define USB_REQ_GET_CONFIGURATION 0x08 -+#define USB_REQ_SET_CONFIGURATION 0x09 -+#define USB_REQ_GET_INTERFACE 0x0A -+#define USB_REQ_SET_INTERFACE 0x0B -+#define USB_REQ_SYNCH_FRAME 0x0C -+ -+/* -+ * USB feature flags are written using USB_REQ_{CLEAR,SET}_FEATURE, and -+ * are read as a bit array returned by USB_REQ_GET_STATUS. (So there -+ * are at most sixteen features of each type.) -+ */ -+#define USB_DEVICE_SELF_POWERED 0 /* (read only) */ -+#define USB_DEVICE_REMOTE_WAKEUP 1 /* dev may initiate wakeup */ -+#define USB_DEVICE_TEST_MODE 2 /* (high speed only) */ -+#define USB_DEVICE_B_HNP_ENABLE 3 /* dev may initiate HNP */ -+#define USB_DEVICE_A_HNP_SUPPORT 4 /* RH port supports HNP */ -+#define USB_DEVICE_A_ALT_HNP_SUPPORT 5 /* other RH port does */ -+#define USB_DEVICE_DEBUG_MODE 6 /* (special devices only) */ -+ -+#define USB_ENDPOINT_HALT 0 /* IN/OUT will STALL */ -+ -+ -+/** -+ * struct usb_ctrlrequest - SETUP data for a USB device control request -+ * @bRequestType: matches the USB bmRequestType field -+ * @bRequest: matches the USB bRequest field -+ * @wValue: matches the USB wValue field (le16 byte order) -+ * @wIndex: matches the USB wIndex field (le16 byte order) -+ * @wLength: matches the USB wLength field (le16 byte order) -+ * -+ * This structure is used to send control requests to a USB device. It matches -+ * the different fields of the USB 2.0 Spec section 9.3, table 9-2. See the -+ * USB spec for a fuller description of the different fields, and what they are -+ * used for. -+ * -+ * Note that the driver for any interface can issue control requests. -+ * For most devices, interfaces don't coordinate with each other, so -+ * such requests may be made at any time. -+ */ -+struct usb_ctrlrequest { -+ __u8 bRequestType; -+ __u8 bRequest; -+ __u16 wValue; -+ __u16 wIndex; -+ __u16 wLength; -+} __attribute__ ((packed)); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* -+ * STANDARD DESCRIPTORS ... as returned by GET_DESCRIPTOR, or -+ * (rarely) accepted by SET_DESCRIPTOR. -+ * -+ * Note that all multi-byte values here are encoded in little endian -+ * byte order "on the wire". But when exposed through Linux-USB APIs, -+ * they've been converted to cpu byte order. -+ */ -+ -+/* -+ * Descriptor types ... USB 2.0 spec table 9.5 -+ */ -+#define USB_DT_DEVICE 0x01 -+#define USB_DT_CONFIG 0x02 -+#define USB_DT_STRING 0x03 -+#define USB_DT_INTERFACE 0x04 -+#define USB_DT_ENDPOINT 0x05 -+#define USB_DT_DEVICE_QUALIFIER 0x06 -+#define USB_DT_OTHER_SPEED_CONFIG 0x07 -+#define USB_DT_INTERFACE_POWER 0x08 -+/* these are from a minor usb 2.0 revision (ECN) */ -+#define USB_DT_OTG 0x09 -+#define USB_DT_DEBUG 0x0a -+#define USB_DT_INTERFACE_ASSOCIATION 0x0b -+ -+/* conventional codes for class-specific descriptors */ -+#define USB_DT_CS_DEVICE 0x21 -+#define USB_DT_CS_CONFIG 0x22 -+#define USB_DT_CS_STRING 0x23 -+#define USB_DT_CS_INTERFACE 0x24 -+#define USB_DT_CS_ENDPOINT 0x25 -+ -+/* All standard descriptors have these 2 fields at the beginning */ -+struct usb_descriptor_header { -+ __u8 bLength; -+ __u8 bDescriptorType; -+} __attribute__ ((packed)); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_DEVICE: Device descriptor */ -+struct usb_device_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u16 bcdUSB; -+ __u8 bDeviceClass; -+ __u8 bDeviceSubClass; -+ __u8 bDeviceProtocol; -+ __u8 bMaxPacketSize0; -+ __u16 idVendor; -+ __u16 idProduct; -+ __u16 bcdDevice; -+ __u8 iManufacturer; -+ __u8 iProduct; -+ __u8 iSerialNumber; -+ __u8 bNumConfigurations; -+} __attribute__ ((packed)); -+ -+#define USB_DT_DEVICE_SIZE 18 -+ -+ -+/* -+ * Device and/or Interface Class codes -+ * as found in bDeviceClass or bInterfaceClass -+ * and defined by www.usb.org documents -+ */ -+#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ -+#define USB_CLASS_AUDIO 1 -+#define USB_CLASS_COMM 2 -+#define USB_CLASS_HID 3 -+#define USB_CLASS_PHYSICAL 5 -+#define USB_CLASS_STILL_IMAGE 6 -+#define USB_CLASS_PRINTER 7 -+#define USB_CLASS_MASS_STORAGE 8 -+#define USB_CLASS_HUB 9 -+#define USB_CLASS_CDC_DATA 0x0a -+#define USB_CLASS_CSCID 0x0b /* chip+ smart card */ -+#define USB_CLASS_CONTENT_SEC 0x0d /* content security */ -+#define USB_CLASS_VIDEO 0x0e -+#define USB_CLASS_APP_SPEC 0xfe -+#define USB_CLASS_VENDOR_SPEC 0xff -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_CONFIG: Configuration descriptor information. -+ * -+ * USB_DT_OTHER_SPEED_CONFIG is the same descriptor, except that the -+ * descriptor type is different. Highspeed-capable devices can look -+ * different depending on what speed they're currently running. Only -+ * devices with a USB_DT_DEVICE_QUALIFIER have any OTHER_SPEED_CONFIG -+ * descriptors. -+ */ -+struct usb_config_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u16 wTotalLength; -+ __u8 bNumInterfaces; -+ __u8 bConfigurationValue; -+ __u8 iConfiguration; -+ __u8 bmAttributes; -+ __u8 bMaxPower; -+} __attribute__ ((packed)); -+ -+#define USB_DT_CONFIG_SIZE 9 -+ -+/* from config descriptor bmAttributes */ -+#define USB_CONFIG_ATT_ONE (1 << 7) /* must be set */ -+#define USB_CONFIG_ATT_SELFPOWER (1 << 6) /* self powered */ -+#define USB_CONFIG_ATT_WAKEUP (1 << 5) /* can wakeup */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_STRING: String descriptor */ -+struct usb_string_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u16 wData[1]; /* UTF-16LE encoded */ -+} __attribute__ ((packed)); -+ -+/* note that "string" zero is special, it holds language codes that -+ * the device supports, not Unicode characters. -+ */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_INTERFACE: Interface descriptor */ -+struct usb_interface_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u8 bInterfaceNumber; -+ __u8 bAlternateSetting; -+ __u8 bNumEndpoints; -+ __u8 bInterfaceClass; -+ __u8 bInterfaceSubClass; -+ __u8 bInterfaceProtocol; -+ __u8 iInterface; -+} __attribute__ ((packed)); -+ -+#define USB_DT_INTERFACE_SIZE 9 -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_ENDPOINT: Endpoint descriptor */ -+struct usb_endpoint_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u8 bEndpointAddress; -+ __u8 bmAttributes; -+ __u16 wMaxPacketSize; -+ __u8 bInterval; -+ -+ // NOTE: these two are _only_ in audio endpoints. -+ // use USB_DT_ENDPOINT*_SIZE in bLength, not sizeof. -+ __u8 bRefresh; -+ __u8 bSynchAddress; -+} __attribute__ ((packed)); -+ -+#define USB_DT_ENDPOINT_SIZE 7 -+#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ -+ -+ -+/* -+ * Endpoints -+ */ -+#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ -+#define USB_ENDPOINT_DIR_MASK 0x80 -+ -+#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ -+#define USB_ENDPOINT_XFER_CONTROL 0 -+#define USB_ENDPOINT_XFER_ISOC 1 -+#define USB_ENDPOINT_XFER_BULK 2 -+#define USB_ENDPOINT_XFER_INT 3 -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_DEVICE_QUALIFIER: Device Qualifier descriptor */ -+struct usb_qualifier_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u16 bcdUSB; -+ __u8 bDeviceClass; -+ __u8 bDeviceSubClass; -+ __u8 bDeviceProtocol; -+ __u8 bMaxPacketSize0; -+ __u8 bNumConfigurations; -+ __u8 bRESERVED; -+} __attribute__ ((packed)); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_OTG (from OTG 1.0a supplement) */ -+struct usb_otg_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u8 bmAttributes; /* support for HNP, SRP, etc */ -+} __attribute__ ((packed)); -+ -+/* from usb_otg_descriptor.bmAttributes */ -+#define USB_OTG_SRP (1 << 0) -+#define USB_OTG_HNP (1 << 1) /* swap host/device roles */ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_DEBUG: for special highspeed devices, replacing serial console */ -+struct usb_debug_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ /* bulk endpoints with 8 byte maxpacket */ -+ __u8 bDebugInEndpoint; -+ __u8 bDebugOutEndpoint; -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB_DT_INTERFACE_ASSOCIATION: groups interfaces */ -+struct usb_interface_assoc_descriptor { -+ __u8 bLength; -+ __u8 bDescriptorType; -+ -+ __u8 bFirstInterface; -+ __u8 bInterfaceCount; -+ __u8 bFunctionClass; -+ __u8 bFunctionSubClass; -+ __u8 bFunctionProtocol; -+ __u8 iFunction; -+} __attribute__ ((packed)); -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* USB 2.0 defines three speeds, here's how Linux identifies them */ -+ -+enum usb_device_speed { -+ USB_SPEED_UNKNOWN = 0, /* enumerating */ -+ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ -+ USB_SPEED_HIGH /* usb 2.0 */ -+}; -+ -+enum usb_device_state { -+ /* NOTATTACHED isn't in the USB spec, and this state acts -+ * the same as ATTACHED ... but it's clearer this way. -+ */ -+ USB_STATE_NOTATTACHED = 0, -+ -+ /* the chapter 9 device states */ -+ USB_STATE_ATTACHED, -+ USB_STATE_POWERED, -+ USB_STATE_DEFAULT, /* limited function */ -+ USB_STATE_ADDRESS, -+ USB_STATE_CONFIGURED, /* most functions */ -+ -+ USB_STATE_SUSPENDED -+ -+ /* NOTE: there are actually four different SUSPENDED -+ * states, returning to POWERED, DEFAULT, ADDRESS, or -+ * CONFIGURED respectively when SOF tokens flow again. -+ */ -+}; -+ -+#endif /* __LINUX_USB_CH9_H */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/include/linux/usb_gadget.h kernel/include/linux/usb_gadget.h ---- /tmp/kernel/include/linux/usb_gadget.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/include/linux/usb_gadget.h 2005-04-22 17:53:19.367550424 +0200 -@@ -0,0 +1,896 @@ -+/* -+ * <linux/usb_gadget.h> -+ * -+ * We call the USB code inside a Linux-based peripheral device a "gadget" -+ * driver, except for the hardware-specific bus glue. One USB host can -+ * master many USB gadgets, but the gadgets are only slaved to one host. -+ * -+ * -+ * (C) Copyright 2002-2004 by David Brownell -+ * All Rights Reserved. -+ * -+ * This software is licensed under the GNU GPL version 2. -+ */ -+ -+#ifndef __LINUX_USB_GADGET_H -+#define __LINUX_USB_GADGET_H -+ -+#ifdef __KERNEL__ -+ -+struct usb_ep; -+ -+/** -+ * struct usb_request - describes one i/o request -+ * @buf: Buffer used for data. Always provide this; some controllers -+ * only use PIO, or don't use DMA for some endpoints. -+ * @dma: DMA address corresponding to 'buf'. If you don't set this -+ * field, and the usb controller needs one, it is responsible -+ * for mapping and unmapping the buffer. -+ * @length: Length of that data -+ * @no_interrupt: If true, hints that no completion irq is needed. -+ * Helpful sometimes with deep request queues that are handled -+ * directly by DMA controllers. -+ * @zero: If true, when writing data, makes the last packet be "short" -+ * by adding a zero length packet as needed; -+ * @short_not_ok: When reading data, makes short packets be -+ * treated as errors (queue stops advancing till cleanup). -+ * @complete: Function called when request completes, so this request and -+ * its buffer may be re-used. -+ * Reads terminate with a short packet, or when the buffer fills, -+ * whichever comes first. When writes terminate, some data bytes -+ * will usually still be in flight (often in a hardware fifo). -+ * Errors (for reads or writes) stop the queue from advancing -+ * until the completion function returns, so that any transfers -+ * invalidated by the error may first be dequeued. -+ * @context: For use by the completion callback -+ * @list: For use by the gadget driver. -+ * @status: Reports completion code, zero or a negative errno. -+ * Normally, faults block the transfer queue from advancing until -+ * the completion callback returns. -+ * Code "-ESHUTDOWN" indicates completion caused by device disconnect, -+ * or when the driver disabled the endpoint. -+ * @actual: Reports bytes transferred to/from the buffer. For reads (OUT -+ * transfers) this may be less than the requested length. If the -+ * short_not_ok flag is set, short reads are treated as errors -+ * even when status otherwise indicates successful completion. -+ * Note that for writes (IN transfers) some data bytes may still -+ * reside in a device-side FIFO when the request is reported as -+ * complete. -+ * -+ * These are allocated/freed through the endpoint they're used with. The -+ * hardware's driver can add extra per-request data to the memory it returns, -+ * which often avoids separate memory allocations (potential failures), -+ * later when the request is queued. -+ * -+ * Request flags affect request handling, such as whether a zero length -+ * packet is written (the "zero" flag), whether a short read should be -+ * treated as an error (blocking request queue advance, the "short_not_ok" -+ * flag), or hinting that an interrupt is not required (the "no_interrupt" -+ * flag, for use with deep request queues). -+ * -+ * Bulk endpoints can use any size buffers, and can also be used for interrupt -+ * transfers. interrupt-only endpoints can be much less functional. -+ */ -+ // NOTE this is analagous to 'struct urb' on the host side, -+ // except that it's thinner and promotes more pre-allocation. -+ -+struct usb_request { -+ void *buf; -+ unsigned length; -+ dma_addr_t dma; -+ -+ unsigned no_interrupt:1; -+ unsigned zero:1; -+ unsigned short_not_ok:1; -+ -+ void (*complete)(struct usb_ep *ep, -+ struct usb_request *req); -+ void *context; -+ struct list_head list; -+ -+ int status; -+ unsigned actual; -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* endpoint-specific parts of the api to the usb controller hardware. -+ * unlike the urb model, (de)multiplexing layers are not required. -+ * (so this api could slash overhead if used on the host side...) -+ * -+ * note that device side usb controllers commonly differ in how many -+ * endpoints they support, as well as their capabilities. -+ */ -+struct usb_ep_ops { -+ int (*enable) (struct usb_ep *ep, -+ const struct usb_endpoint_descriptor *desc); -+ int (*disable) (struct usb_ep *ep); -+ -+ struct usb_request *(*alloc_request) (struct usb_ep *ep, -+ int gfp_flags); -+ void (*free_request) (struct usb_ep *ep, struct usb_request *req); -+ -+ void *(*alloc_buffer) (struct usb_ep *ep, unsigned bytes, -+ dma_addr_t *dma, int gfp_flags); -+ void (*free_buffer) (struct usb_ep *ep, void *buf, dma_addr_t dma, -+ unsigned bytes); -+ // NOTE: on 2.6, drivers may also use dma_map() and -+ // dma_sync_single_*() to directly manage dma overhead. -+ -+ int (*queue) (struct usb_ep *ep, struct usb_request *req, -+ int gfp_flags); -+ int (*dequeue) (struct usb_ep *ep, struct usb_request *req); -+ -+ int (*set_halt) (struct usb_ep *ep, int value); -+ int (*fifo_status) (struct usb_ep *ep); -+ void (*fifo_flush) (struct usb_ep *ep); -+}; -+ -+/** -+ * struct usb_ep - device side representation of USB endpoint -+ * @name:identifier for the endpoint, such as "ep-a" or "ep9in-bulk" -+ * @ops: Function pointers used to access hardware-specific operations. -+ * @ep_list:the gadget's ep_list holds all of its endpoints -+ * @maxpacket:The maximum packet size used on this endpoint. The initial -+ * value can sometimes be reduced (hardware allowing), according to -+ * the endpoint descriptor used to configure the endpoint. -+ * @driver_data:for use by the gadget driver. all other fields are -+ * read-only to gadget drivers. -+ * -+ * the bus controller driver lists all the general purpose endpoints in -+ * gadget->ep_list. the control endpoint (gadget->ep0) is not in that list, -+ * and is accessed only in response to a driver setup() callback. -+ */ -+struct usb_ep { -+ void *driver_data; -+ -+ const char *name; -+ const struct usb_ep_ops *ops; -+ struct list_head ep_list; -+ unsigned maxpacket:16; -+}; -+ -+/*-------------------------------------------------------------------------*/ -+ -+/** -+ * usb_ep_enable - configure endpoint, making it usable -+ * @ep:the endpoint being configured. may not be the endpoint named "ep0". -+ * drivers discover endpoints through the ep_list of a usb_gadget. -+ * @desc:descriptor for desired behavior. caller guarantees this pointer -+ * remains valid until the endpoint is disabled; the data byte order -+ * is little-endian (usb-standard). -+ * -+ * when configurations are set, or when interface settings change, the driver -+ * will enable or disable the relevant endpoints. while it is enabled, an -+ * endpoint may be used for i/o until the driver receives a disconnect() from -+ * the host or until the endpoint is disabled. -+ * -+ * the ep0 implementation (which calls this routine) must ensure that the -+ * hardware capabilities of each endpoint match the descriptor provided -+ * for it. for example, an endpoint named "ep2in-bulk" would be usable -+ * for interrupt transfers as well as bulk, but it likely couldn't be used -+ * for iso transfers or for endpoint 14. some endpoints are fully -+ * configurable, with more generic names like "ep-a". (remember that for -+ * USB, "in" means "towards the USB master".) -+ * -+ * returns zero, or a negative error code. -+ */ -+static inline int -+usb_ep_enable (struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) -+{ -+ return ep->ops->enable (ep, desc); -+} -+ -+/** -+ * usb_ep_disable - endpoint is no longer usable -+ * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0". -+ * -+ * no other task may be using this endpoint when this is called. -+ * any pending and uncompleted requests will complete with status -+ * indicating disconnect (-ESHUTDOWN) before this call returns. -+ * gadget drivers must call usb_ep_enable() again before queueing -+ * requests to the endpoint. -+ * -+ * returns zero, or a negative error code. -+ */ -+static inline int -+usb_ep_disable (struct usb_ep *ep) -+{ -+ return ep->ops->disable (ep); -+} -+ -+/** -+ * usb_ep_alloc_request - allocate a request object to use with this endpoint -+ * @ep:the endpoint to be used with with the request -+ * @gfp_flags:GFP_* flags to use -+ * -+ * Request objects must be allocated with this call, since they normally -+ * need controller-specific setup and may even need endpoint-specific -+ * resources such as allocation of DMA descriptors. -+ * Requests may be submitted with usb_ep_queue(), and receive a single -+ * completion callback. Free requests with usb_ep_free_request(), when -+ * they are no longer needed. -+ * -+ * Returns the request, or null if one could not be allocated. -+ */ -+static inline struct usb_request * -+usb_ep_alloc_request (struct usb_ep *ep, int gfp_flags) -+{ -+ return ep->ops->alloc_request (ep, gfp_flags); -+} -+ -+/** -+ * usb_ep_free_request - frees a request object -+ * @ep:the endpoint associated with the request -+ * @req:the request being freed -+ * -+ * Reverses the effect of usb_ep_alloc_request(). -+ * Caller guarantees the request is not queued, and that it will -+ * no longer be requeued (or otherwise used). -+ */ -+static inline void -+usb_ep_free_request (struct usb_ep *ep, struct usb_request *req) -+{ -+ ep->ops->free_request (ep, req); -+} -+ -+/** -+ * usb_ep_alloc_buffer - allocate an I/O buffer -+ * @ep:the endpoint associated with the buffer -+ * @len:length of the desired buffer -+ * @dma:pointer to the buffer's DMA address; must be valid -+ * @gfp_flags:GFP_* flags to use -+ * -+ * Returns a new buffer, or null if one could not be allocated. -+ * The buffer is suitably aligned for dma, if that endpoint uses DMA, -+ * and the caller won't have to care about dma-inconsistency -+ * or any hidden "bounce buffer" mechanism. No additional per-request -+ * DMA mapping will be required for such buffers. -+ * Free it later with usb_ep_free_buffer(). -+ * -+ * You don't need to use this call to allocate I/O buffers unless you -+ * want to make sure drivers don't incur costs for such "bounce buffer" -+ * copies or per-request DMA mappings. -+ */ -+static inline void * -+usb_ep_alloc_buffer (struct usb_ep *ep, unsigned len, dma_addr_t *dma, -+ int gfp_flags) -+{ -+ return ep->ops->alloc_buffer (ep, len, dma, gfp_flags); -+} -+ -+/** -+ * usb_ep_free_buffer - frees an i/o buffer -+ * @ep:the endpoint associated with the buffer -+ * @buf:CPU view address of the buffer -+ * @dma:the buffer's DMA address -+ * @len:length of the buffer -+ * -+ * reverses the effect of usb_ep_alloc_buffer(). -+ * caller guarantees the buffer will no longer be accessed -+ */ -+static inline void -+usb_ep_free_buffer (struct usb_ep *ep, void *buf, dma_addr_t dma, unsigned len) -+{ -+ ep->ops->free_buffer (ep, buf, dma, len); -+} -+ -+/** -+ * usb_ep_queue - queues (submits) an I/O request to an endpoint. -+ * @ep:the endpoint associated with the request -+ * @req:the request being submitted -+ * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't -+ * pre-allocate all necessary memory with the request. -+ * -+ * This tells the device controller to perform the specified request through -+ * that endpoint (reading or writing a buffer). When the request completes, -+ * including being canceled by usb_ep_dequeue(), the request's completion -+ * routine is called to return the request to the driver. Any endpoint -+ * (except control endpoints like ep0) may have more than one transfer -+ * request queued; they complete in FIFO order. Once a gadget driver -+ * submits a request, that request may not be examined or modified until it -+ * is given back to that driver through the completion callback. -+ * -+ * Each request is turned into one or more packets. The controller driver -+ * never merges adjacent requests into the same packet. OUT transfers -+ * will sometimes use data that's already buffered in the hardware. -+ * Drivers can rely on the fact that the first byte of the request's buffer -+ * always corresponds to the first byte of some USB packet, for both -+ * IN and OUT transfers. -+ * -+ * Bulk endpoints can queue any amount of data; the transfer is packetized -+ * automatically. The last packet will be short if the request doesn't fill it -+ * out completely. Zero length packets (ZLPs) should be avoided in portable -+ * protocols since not all usb hardware can successfully handle zero length -+ * packets. (ZLPs may be explicitly written, and may be implicitly written if -+ * the request 'zero' flag is set.) Bulk endpoints may also be used -+ * for interrupt transfers; but the reverse is not true, and some endpoints -+ * won't support every interrupt transfer. (Such as 768 byte packets.) -+ * -+ * Interrupt-only endpoints are less functional than bulk endpoints, for -+ * example by not supporting queueing or not handling buffers that are -+ * larger than the endpoint's maxpacket size. They may also treat data -+ * toggle differently. -+ * -+ * Control endpoints ... after getting a setup() callback, the driver queues -+ * one response (even if it would be zero length). That enables the -+ * status ack, after transfering data as specified in the response. Setup -+ * functions may return negative error codes to generate protocol stalls. -+ * (Note that some USB device controllers disallow protocol stall responses -+ * in some cases.) When control responses are deferred (the response is -+ * written after the setup callback returns), then usb_ep_set_halt() may be -+ * used on ep0 to trigger protocol stalls. -+ * -+ * For periodic endpoints, like interrupt or isochronous ones, the usb host -+ * arranges to poll once per interval, and the gadget driver usually will -+ * have queued some data to transfer at that time. -+ * -+ * Returns zero, or a negative error code. Endpoints that are not enabled -+ * report errors; errors will also be -+ * reported when the usb peripheral is disconnected. -+ */ -+static inline int -+usb_ep_queue (struct usb_ep *ep, struct usb_request *req, int gfp_flags) -+{ -+ return ep->ops->queue (ep, req, gfp_flags); -+} -+ -+/** -+ * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint -+ * @ep:the endpoint associated with the request -+ * @req:the request being canceled -+ * -+ * if the request is still active on the endpoint, it is dequeued and its -+ * completion routine is called (with status -ECONNRESET); else a negative -+ * error code is returned. -+ * -+ * note that some hardware can't clear out write fifos (to unlink the request -+ * at the head of the queue) except as part of disconnecting from usb. such -+ * restrictions prevent drivers from supporting configuration changes, -+ * even to configuration zero (a "chapter 9" requirement). -+ */ -+static inline int usb_ep_dequeue (struct usb_ep *ep, struct usb_request *req) -+{ -+ return ep->ops->dequeue (ep, req); -+} -+ -+/** -+ * usb_ep_set_halt - sets the endpoint halt feature. -+ * @ep: the non-isochronous endpoint being stalled -+ * -+ * Use this to stall an endpoint, perhaps as an error report. -+ * Except for control endpoints, -+ * the endpoint stays halted (will not stream any data) until the host -+ * clears this feature; drivers may need to empty the endpoint's request -+ * queue first, to make sure no inappropriate transfers happen. -+ * -+ * Note that while an endpoint CLEAR_FEATURE will be invisible to the -+ * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the -+ * current altsetting, see usb_ep_clear_halt(). When switching altsettings, -+ * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints. -+ * -+ * Returns zero, or a negative error code. On success, this call sets -+ * underlying hardware state that blocks data transfers. -+ * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any -+ * transfer requests are still queued, or if the controller hardware -+ * (usually a FIFO) still holds bytes that the host hasn't collected. -+ */ -+static inline int -+usb_ep_set_halt (struct usb_ep *ep) -+{ -+ return ep->ops->set_halt (ep, 1); -+} -+ -+/** -+ * usb_ep_clear_halt - clears endpoint halt, and resets toggle -+ * @ep:the bulk or interrupt endpoint being reset -+ * -+ * Use this when responding to the standard usb "set interface" request, -+ * for endpoints that aren't reconfigured, after clearing any other state -+ * in the endpoint's i/o queue. -+ * -+ * Returns zero, or a negative error code. On success, this call clears -+ * the underlying hardware state reflecting endpoint halt and data toggle. -+ * Note that some hardware can't support this request (like pxa2xx_udc), -+ * and accordingly can't correctly implement interface altsettings. -+ */ -+static inline int -+usb_ep_clear_halt (struct usb_ep *ep) -+{ -+ return ep->ops->set_halt (ep, 0); -+} -+ -+/** -+ * usb_ep_fifo_status - returns number of bytes in fifo, or error -+ * @ep: the endpoint whose fifo status is being checked. -+ * -+ * FIFO endpoints may have "unclaimed data" in them in certain cases, -+ * such as after aborted transfers. Hosts may not have collected all -+ * the IN data written by the gadget driver (and reported by a request -+ * completion). The gadget driver may not have collected all the data -+ * written OUT to it by the host. Drivers that need precise handling for -+ * fault reporting or recovery may need to use this call. -+ * -+ * This returns the number of such bytes in the fifo, or a negative -+ * errno if the endpoint doesn't use a FIFO or doesn't support such -+ * precise handling. -+ */ -+static inline int -+usb_ep_fifo_status (struct usb_ep *ep) -+{ -+ if (ep->ops->fifo_status) -+ return ep->ops->fifo_status (ep); -+ else -+ return -EOPNOTSUPP; -+} -+ -+/** -+ * usb_ep_fifo_flush - flushes contents of a fifo -+ * @ep: the endpoint whose fifo is being flushed. -+ * -+ * This call may be used to flush the "unclaimed data" that may exist in -+ * an endpoint fifo after abnormal transaction terminations. The call -+ * must never be used except when endpoint is not being used for any -+ * protocol translation. -+ */ -+static inline void -+usb_ep_fifo_flush (struct usb_ep *ep) -+{ -+ if (ep->ops->fifo_flush) -+ ep->ops->fifo_flush (ep); -+} -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+struct usb_gadget; -+ -+/* the rest of the api to the controller hardware: device operations, -+ * which don't involve endpoints (or i/o). -+ */ -+struct usb_gadget_ops { -+ int (*get_frame)(struct usb_gadget *); -+ int (*wakeup)(struct usb_gadget *); -+ int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered); -+ int (*vbus_session) (struct usb_gadget *, int is_active); -+ int (*vbus_draw) (struct usb_gadget *, unsigned mA); -+ int (*pullup) (struct usb_gadget *, int is_on); -+ int (*ioctl)(struct usb_gadget *, -+ unsigned code, unsigned long param); -+}; -+ -+/** -+ * struct usb_gadget - represents a usb slave device -+ * @ops: Function pointers used to access hardware-specific operations. -+ * @ep0: Endpoint zero, used when reading or writing responses to -+ * driver setup() requests -+ * @ep_list: List of other endpoints supported by the device. -+ * @speed: Speed of current connection to USB host. -+ * @is_dualspeed: True if the controller supports both high and full speed -+ * operation. If it does, the gadget driver must also support both. -+ * @is_otg: True if the USB device port uses a Mini-AB jack, so that the -+ * gadget driver must provide a USB OTG descriptor. -+ * @is_a_peripheral: False unless is_otg, the "A" end of a USB cable -+ * is in the Mini-AB jack, and HNP has been used to switch roles -+ * so that the "A" device currently acts as A-Peripheral, not A-Host. -+ * @a_hnp_support: OTG device feature flag, indicating that the A-Host -+ * supports HNP at this port. -+ * @a_alt_hnp_support: OTG device feature flag, indicating that the A-Host -+ * only supports HNP on a different root port. -+ * @b_hnp_enable: OTG device feature flag, indicating that the A-Host -+ * enabled HNP support. -+ * @name: Identifies the controller hardware type. Used in diagnostics -+ * and sometimes configuration. -+ * @dev: Driver model state for this abstract device. -+ * -+ * Gadgets have a mostly-portable "gadget driver" implementing device -+ * functions, handling all usb configurations and interfaces. Gadget -+ * drivers talk to hardware-specific code indirectly, through ops vectors. -+ * That insulates the gadget driver from hardware details, and packages -+ * the hardware endpoints through generic i/o queues. The "usb_gadget" -+ * and "usb_ep" interfaces provide that insulation from the hardware. -+ * -+ * Except for the driver data, all fields in this structure are -+ * read-only to the gadget driver. That driver data is part of the -+ * "driver model" infrastructure in 2.6 (and later) kernels, and for -+ * earlier systems is grouped in a similar structure that's not known -+ * to the rest of the kernel. -+ * -+ * Values of the three OTG device feature flags are updated before the -+ * setup() call corresponding to USB_REQ_SET_CONFIGURATION, and before -+ * driver suspend() calls. They are valid only when is_otg, and when the -+ * device is acting as a B-Peripheral (so is_a_peripheral is false). -+ */ -+struct usb_gadget { -+ /* readonly to gadget driver */ -+ const struct usb_gadget_ops *ops; -+ struct usb_ep *ep0; -+ struct list_head ep_list; /* of usb_ep */ -+ enum usb_device_speed speed; -+ unsigned is_dualspeed:1; -+ unsigned is_otg:1; -+ unsigned is_a_peripheral:1; -+ unsigned b_hnp_enable:1; -+ unsigned a_hnp_support:1; -+ unsigned a_alt_hnp_support:1; -+ const char *name; -+ -+ struct __gadget_device { -+ const char *bus_id; -+ void *driver_data; -+ } dev; -+}; -+ -+static inline void set_gadget_data (struct usb_gadget *gadget, void *data) -+ { gadget->dev.driver_data = data; } -+static inline void *get_gadget_data (struct usb_gadget *gadget) -+ { return gadget->dev.driver_data; } -+ -+ -+/* iterates the non-control endpoints; 'tmp' is a struct usb_ep pointer */ -+#define gadget_for_each_ep(tmp,gadget) \ -+ list_for_each_entry(tmp, &(gadget)->ep_list, ep_list) -+ -+#ifndef list_for_each_entry -+/* not available in 2.4.18 */ -+#define list_for_each_entry(pos, head, member) \ -+ for (pos = list_entry((head)->next, typeof(*pos), member), \ -+ prefetch(pos->member.next); \ -+ &pos->member != (head); \ -+ pos = list_entry(pos->member.next, typeof(*pos), member), \ -+ prefetch(pos->member.next)) -+#endif -+ -+ -+/** -+ * usb_gadget_frame_number - returns the current frame number -+ * @gadget: controller that reports the frame number -+ * -+ * Returns the usb frame number, normally eleven bits from a SOF packet, -+ * or negative errno if this device doesn't support this capability. -+ */ -+static inline int usb_gadget_frame_number (struct usb_gadget *gadget) -+{ -+ return gadget->ops->get_frame (gadget); -+} -+ -+/** -+ * usb_gadget_wakeup - tries to wake up the host connected to this gadget -+ * @gadget: controller used to wake up the host -+ * -+ * Returns zero on success, else negative error code if the hardware -+ * doesn't support such attempts, or its support has not been enabled -+ * by the usb host. Drivers must return device descriptors that report -+ * their ability to support this, or hosts won't enable it. -+ * -+ * This may also try to use SRP to wake the host and start enumeration, -+ * even if OTG isn't otherwise in use. OTG devices may also start -+ * remote wakeup even when hosts don't explicitly enable it. -+ */ -+static inline int usb_gadget_wakeup (struct usb_gadget *gadget) -+{ -+ if (!gadget->ops->wakeup) -+ return -EOPNOTSUPP; -+ return gadget->ops->wakeup (gadget); -+} -+ -+/** -+ * usb_gadget_set_selfpowered - sets the device selfpowered feature. -+ * @gadget:the device being declared as self-powered -+ * -+ * this affects the device status reported by the hardware driver -+ * to reflect that it now has a local power supply. -+ * -+ * returns zero on success, else negative errno. -+ */ -+static inline int -+usb_gadget_set_selfpowered (struct usb_gadget *gadget) -+{ -+ if (!gadget->ops->set_selfpowered) -+ return -EOPNOTSUPP; -+ return gadget->ops->set_selfpowered (gadget, 1); -+} -+ -+/** -+ * usb_gadget_clear_selfpowered - clear the device selfpowered feature. -+ * @gadget:the device being declared as bus-powered -+ * -+ * this affects the device status reported by the hardware driver. -+ * some hardware may not support bus-powered operation, in which -+ * case this feature's value can never change. -+ * -+ * returns zero on success, else negative errno. -+ */ -+static inline int -+usb_gadget_clear_selfpowered (struct usb_gadget *gadget) -+{ -+ if (!gadget->ops->set_selfpowered) -+ return -EOPNOTSUPP; -+ return gadget->ops->set_selfpowered (gadget, 0); -+} -+ -+/** -+ * usb_gadget_vbus_connect - Notify controller that VBUS is powered -+ * @gadget:The device which now has VBUS power. -+ * -+ * This call is used by a driver for an external transceiver (or GPIO) -+ * that detects a VBUS power session starting. Common responses include -+ * resuming the controller, activating the D+ (or D-) pullup to let the -+ * host detect that a USB device is attached, and starting to draw power -+ * (8mA or possibly more, especially after SET_CONFIGURATION). -+ * -+ * Returns zero on success, else negative errno. -+ */ -+static inline int -+usb_gadget_vbus_connect(struct usb_gadget *gadget) -+{ -+ if (!gadget->ops->vbus_session) -+ return -EOPNOTSUPP; -+ return gadget->ops->vbus_session (gadget, 1); -+} -+ -+/** -+ * usb_gadget_vbus_draw - constrain controller's VBUS power usage -+ * @gadget:The device whose VBUS usage is being described -+ * @mA:How much current to draw, in milliAmperes. This should be twice -+ * the value listed in the configuration descriptor bMaxPower field. -+ * -+ * This call is used by gadget drivers during SET_CONFIGURATION calls, -+ * reporting how much power the device may consume. For example, this -+ * could affect how quickly batteries are recharged. -+ * -+ * Returns zero on success, else negative errno. -+ */ -+static inline int -+usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) -+{ -+ if (!gadget->ops->vbus_draw) -+ return -EOPNOTSUPP; -+ return gadget->ops->vbus_draw (gadget, mA); -+} -+ -+/** -+ * usb_gadget_vbus_disconnect - notify controller about VBUS session end -+ * @gadget:the device whose VBUS supply is being described -+ * -+ * This call is used by a driver for an external transceiver (or GPIO) -+ * that detects a VBUS power session ending. Common responses include -+ * reversing everything done in usb_gadget_vbus_connect(). -+ * -+ * Returns zero on success, else negative errno. -+ */ -+static inline int -+usb_gadget_vbus_disconnect(struct usb_gadget *gadget) -+{ -+ if (!gadget->ops->vbus_session) -+ return -EOPNOTSUPP; -+ return gadget->ops->vbus_session (gadget, 0); -+} -+ -+/** -+ * usb_gadget_connect - software-controlled connect to USB host -+ * @gadget:the peripheral being connected -+ * -+ * Enables the D+ (or potentially D-) pullup. The host will start -+ * enumerating this gadget when the pullup is active and a VBUS session -+ * is active (the link is powered). This pullup is always enabled unless -+ * usb_gadget_disconnect() has been used to disable it. -+ * -+ * Returns zero on success, else negative errno. -+ */ -+static inline int -+usb_gadget_connect (struct usb_gadget *gadget) -+{ -+ if (!gadget->ops->pullup) -+ return -EOPNOTSUPP; -+ return gadget->ops->pullup (gadget, 1); -+} -+ -+/** -+ * usb_gadget_disconnect - software-controlled disconnect from USB host -+ * @gadget:the peripheral being disconnected -+ * -+ * Disables the D+ (or potentially D-) pullup, which the host may see -+ * as a disconnect (when a VBUS session is active). Not all systems -+ * support software pullup controls. -+ * -+ * This routine may be used during the gadget driver bind() call to prevent -+ * the peripheral from ever being visible to the USB host, unless later -+ * usb_gadget_connect() is called. For example, user mode components may -+ * need to be activated before the system can talk to hosts. -+ * -+ * Returns zero on success, else negative errno. -+ */ -+static inline int -+usb_gadget_disconnect (struct usb_gadget *gadget) -+{ -+ if (!gadget->ops->pullup) -+ return -EOPNOTSUPP; -+ return gadget->ops->pullup (gadget, 0); -+} -+ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/** -+ * struct usb_gadget_driver - driver for usb 'slave' devices -+ * @function: String describing the gadget's function -+ * @speed: Highest speed the driver handles. -+ * @bind: Invoked when the driver is bound to a gadget, usually -+ * after registering the driver. -+ * At that point, ep0 is fully initialized, and ep_list holds -+ * the currently-available endpoints. -+ * Called in a context that permits sleeping. -+ * @setup: Invoked for ep0 control requests that aren't handled by -+ * the hardware level driver. Most calls must be handled by -+ * the gadget driver, including descriptor and configuration -+ * management. The 16 bit members of the setup data are in -+ * cpu order. Called in_interrupt; this may not sleep. Driver -+ * queues a response to ep0, or returns negative to stall. -+ * @disconnect: Invoked after all transfers have been stopped, -+ * when the host is disconnected. May be called in_interrupt; this -+ * may not sleep. Some devices can't detect disconnect, so this might -+ * not be called except as part of controller shutdown. -+ * @unbind: Invoked when the driver is unbound from a gadget, -+ * usually from rmmod (after a disconnect is reported). -+ * Called in a context that permits sleeping. -+ * @suspend: Invoked on USB suspend. May be called in_interrupt. -+ * @resume: Invoked on USB resume. May be called in_interrupt. -+ * @driver: Driver model state for this driver. -+ * -+ * Devices are disabled till a gadget driver successfully bind()s, which -+ * means the driver will handle setup() requests needed to enumerate (and -+ * meet "chapter 9" requirements) then do some useful work. -+ * -+ * If gadget->is_otg is true, the gadget driver must provide an OTG -+ * descriptor during enumeration, or else fail the bind() call. In such -+ * cases, no USB traffic may flow until both bind() returns without -+ * having called usb_gadget_disconnect(), and the USB host stack has -+ * initialized. -+ * -+ * Drivers use hardware-specific knowledge to configure the usb hardware. -+ * endpoint addressing is only one of several hardware characteristics that -+ * are in descriptors the ep0 implementation returns from setup() calls. -+ * -+ * Except for ep0 implementation, most driver code shouldn't need change to -+ * run on top of different usb controllers. It'll use endpoints set up by -+ * that ep0 implementation. -+ * -+ * The usb controller driver handles a few standard usb requests. Those -+ * include set_address, and feature flags for devices, interfaces, and -+ * endpoints (the get_status, set_feature, and clear_feature requests). -+ * -+ * Accordingly, the driver's setup() callback must always implement all -+ * get_descriptor requests, returning at least a device descriptor and -+ * a configuration descriptor. Drivers must make sure the endpoint -+ * descriptors match any hardware constraints. Some hardware also constrains -+ * other descriptors. (The pxa250 allows only configurations 1, 2, or 3). -+ * -+ * The driver's setup() callback must also implement set_configuration, -+ * and should also implement set_interface, get_configuration, and -+ * get_interface. Setting a configuration (or interface) is where -+ * endpoints should be activated or (config 0) shut down. -+ * -+ * (Note that only the default control endpoint is supported. Neither -+ * hosts nor devices generally support control traffic except to ep0.) -+ * -+ * Most devices will ignore USB suspend/resume operations, and so will -+ * not provide those callbacks. However, some may need to change modes -+ * when the host is not longer directing those activities. For example, -+ * local controls (buttons, dials, etc) may need to be re-enabled since -+ * the (remote) host can't do that any longer; or an error state might -+ * be cleared, to make the device behave identically whether or not -+ * power is maintained. -+ */ -+struct usb_gadget_driver { -+ char *function; -+ enum usb_device_speed speed; -+ int (*bind)(struct usb_gadget *); -+ void (*unbind)(struct usb_gadget *); -+ int (*setup)(struct usb_gadget *, -+ const struct usb_ctrlrequest *); -+ void (*disconnect)(struct usb_gadget *); -+ void (*suspend)(struct usb_gadget *); -+ void (*resume)(struct usb_gadget *); -+ -+ // FIXME support safe rmmod -+ struct __gadget_driver { -+ const char *name; -+ void *driver_data; -+ } driver; -+}; -+ -+ -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* driver modules register and unregister, as usual. -+ * these calls must be made in a context that can sleep. -+ * -+ * these will usually be implemented directly by the hardware-dependent -+ * usb bus interface driver, which will only support a single driver. -+ */ -+ -+/** -+ * usb_gadget_register_driver - register a gadget driver -+ * @driver:the driver being registered -+ * -+ * Call this in your gadget driver's module initialization function, -+ * to tell the underlying usb controller driver about your driver. -+ * The driver's bind() function will be called to bind it to a -+ * gadget. This function must be called in a context that can sleep. -+ */ -+int usb_gadget_register_driver (struct usb_gadget_driver *driver); -+ -+/** -+ * usb_gadget_unregister_driver - unregister a gadget driver -+ * @driver:the driver being unregistered -+ * -+ * Call this in your gadget driver's module cleanup function, -+ * to tell the underlying usb controller that your driver is -+ * going away. If the controller is connected to a USB host, -+ * it will first disconnect(). The driver is also requested -+ * to unbind() and clean up any device state, before this procedure -+ * finally returns. -+ * This function must be called in a context that can sleep. -+ */ -+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* utility to simplify dealing with string descriptors */ -+ -+/** -+ * struct usb_string - wraps a C string and its USB id -+ * @id:the (nonzero) ID for this string -+ * @s:the string, in UTF-8 encoding -+ * -+ * If you're using usb_gadget_get_string(), use this to wrap a string -+ * together with its ID. -+ */ -+struct usb_string { -+ u8 id; -+ const char *s; -+}; -+ -+/** -+ * struct usb_gadget_strings - a set of USB strings in a given language -+ * @language:identifies the strings' language (0x0409 for en-us) -+ * @strings:array of strings with their ids -+ * -+ * If you're using usb_gadget_get_string(), use this to wrap all the -+ * strings for a given language. -+ */ -+struct usb_gadget_strings { -+ u16 language; /* 0x0409 for en-us */ -+ struct usb_string *strings; -+}; -+ -+/* put descriptor for string with that id into buf (buflen >= 256) */ -+int usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* utility to simplify managing config descriptors */ -+ -+/* write vector of descriptors into buffer */ -+int usb_descriptor_fillbuf(void *, unsigned, -+ const struct usb_descriptor_header **); -+ -+/* build config descriptor from single descriptor vector */ -+int usb_gadget_config_buf(const struct usb_config_descriptor *config, -+ void *buf, unsigned buflen, const struct usb_descriptor_header **desc); -+ -+/*-------------------------------------------------------------------------*/ -+ -+/* utility wrapping a simple endpoint selection policy */ -+ -+extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *, -+ struct usb_endpoint_descriptor *) __init; -+ -+extern void usb_ep_autoconfig_reset (struct usb_gadget *) __init; -+ -+#endif /* __KERNEL__ */ -+ -+#endif /* __LINUX_USB_GADGET_H */ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/include/linux/usb_gadgetfs.h kernel/include/linux/usb_gadgetfs.h ---- /tmp/kernel/include/linux/usb_gadgetfs.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/include/linux/usb_gadgetfs.h 2005-04-22 17:53:19.370549935 +0200 -@@ -0,0 +1,75 @@ -+ -+#include <asm/types.h> -+#include <asm/ioctl.h> -+ -+#include <linux/usb_ch9.h> -+ -+/* -+ * Filesystem based user-mode API to USB Gadget controller hardware -+ * -+ * Almost everything can be done with only read and write operations, -+ * on endpoint files found in one directory. They are configured by -+ * writing descriptors, and then may be used for normal stream style -+ * i/o requests. When ep0 is configured, the device can enumerate; -+ * when it's closed, the device disconnects from usb. -+ * -+ * Configuration and device descriptors get written to /dev/gadget/$CHIP, -+ * which may then be used to read usb_gadgetfs_event structs. The driver -+ * may activate endpoints as it handles SET_CONFIGURATION setup events, -+ * or earlier; writing endpoint descriptors to /dev/gadget/$ENDPOINT -+ * then performing data transfers by reading or writing. -+ */ -+ -+/* -+ * Events are delivered on the ep0 file descriptor, if the user mode driver -+ * reads from this file descriptor after writing the descriptors. Don't -+ * stop polling this descriptor, if you write that kind of driver. -+ */ -+ -+enum usb_gadgetfs_event_type { -+ GADGETFS_NOP = 0, -+ -+ GADGETFS_CONNECT, -+ GADGETFS_DISCONNECT, -+ GADGETFS_SETUP, -+ GADGETFS_SUSPEND, -+ // and likely more ! -+}; -+ -+struct usb_gadgetfs_event { -+ enum usb_gadgetfs_event_type type; -+ union { -+ // NOP, DISCONNECT, SUSPEND: nothing -+ // ... some hardware can't report disconnection -+ -+ // CONNECT: just the speed -+ enum usb_device_speed speed; -+ -+ // SETUP: packet; DATA phase i/o precedes next event -+ // (setup.bmRequestType & USB_DIR_IN) flags direction -+ // ... includes SET_CONFIGURATION, SET_INTERFACE -+ struct usb_ctrlrequest setup; -+ } u; -+}; -+ -+ -+/* endpoint ioctls */ -+ -+/* IN transfers may be reported to the gadget driver as complete -+ * when the fifo is loaded, before the host reads the data; -+ * OUT transfers may be reported to the host's "client" driver as -+ * complete when they're sitting in the FIFO unread. -+ * THIS returns how many bytes are "unclaimed" in the endpoint fifo -+ * (needed for precise fault handling, when the hardware allows it) -+ */ -+#define GADGETFS_FIFO_STATUS _IO('g',1) -+ -+/* discards any unclaimed data in the fifo. */ -+#define GADGETFS_FIFO_FLUSH _IO('g',2) -+ -+/* resets endpoint halt+toggle; used to implement set_interface. -+ * some hardware (like pxa2xx) can't support this. -+ */ -+#define GADGETFS_CLEAR_HALT _IO('g',3) -+ -+ -diff -x '*~' -x '.*' -r -N -u /tmp/kernel/include/linux/usb_scanner_ioctl.h kernel/include/linux/usb_scanner_ioctl.h ---- /tmp/kernel/include/linux/usb_scanner_ioctl.h 1970-01-01 01:00:00.000000000 +0100 -+++ kernel/include/linux/usb_scanner_ioctl.h 2005-04-22 17:53:19.372549610 +0200 -@@ -0,0 +1,9 @@ -+/* USB Scanner IOCTLS */ -+ -+/* read vendor and product IDs from the scanner */ -+#define SCANNER_IOCTL_VENDOR _IOR('U', 0x20, int) -+#define SCANNER_IOCTL_PRODUCT _IOR('U', 0x21, int) -+/* send/recv a control message to the scanner */ -+#define SCANNER_IOCTL_CTRLMSG _IOWR('U', 0x22, struct usb_ctrlrequest ) -+ -+ diff --git a/packages/linux/files/linux-2.4.18-list_move.patch b/packages/linux/files/linux-2.4.18-list_move.patch deleted file mode 100644 index faec56330b..0000000000 --- a/packages/linux/files/linux-2.4.18-list_move.patch +++ /dev/null @@ -1,32 +0,0 @@ ---- linux/include/linux/list.h~ 2001-12-21 17:42:03.000000000 +0000 -+++ linux/include/linux/list.h 2004-06-14 23:41:33.000000000 +0100 -@@ -105,6 +105,29 @@ - } - - /** -+ * list_move - delete from one list and add as another's head -+ * @list: the entry to move -+ * @head: the head that will precede our entry -+ */ -+static inline void list_move(struct list_head *list, struct list_head *head) -+{ -+ __list_del(list->prev, list->next); -+ list_add(list, head); -+} -+ -+/** -+ * list_move_tail - delete from one list and add as another's tail -+ * @list: the entry to move -+ * @head: the head that will follow our entry -+ */ -+static inline void list_move_tail(struct list_head *list, -+ struct list_head *head) -+{ -+ __list_del(list->prev, list->next); -+ list_add_tail(list, head); -+} -+ -+/** - * list_empty - tests whether a list is empty - * @head: the list to test. - */ diff --git a/packages/linux/files/mipv6-1.1-v2.4.25.patch b/packages/linux/files/mipv6-1.1-v2.4.25.patch deleted file mode 100644 index b7f9b8fc8a..0000000000 --- a/packages/linux/files/mipv6-1.1-v2.4.25.patch +++ /dev/null @@ -1,19832 +0,0 @@ -diff -uprN linux-2.4.25.old/Documentation/Configure.help linux-2.4.25/Documentation/Configure.help ---- linux-2.4.25.old/Documentation/Configure.help 2004-06-26 11:22:00.000000000 +0100 -+++ linux-2.4.25/Documentation/Configure.help 2004-06-26 11:29:29.000000000 +0100 -@@ -6204,6 +6204,57 @@ CONFIG_IPV6 - - It is safe to say N here for now. - -+IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL) -+CONFIG_IPV6_TUNNEL -+ Experimental IP6-IP6 tunneling. You must select this, if you want -+ to use CONFIG_IPV6_MOBILITY. More information in MIPL Mobile IPv6 -+ instructions. -+ -+ If you don't want IP6-IP6 tunnels and Mobile IPv6, say N. -+ -+IPv6: Mobility Support (EXPERIMENTAL) -+CONFIG_IPV6_MOBILITY -+ This is experimental support for the upcoming specification of -+ Mobile IPv6. Mobile IPv6 allows nodes to seamlessly move between -+ networks without changing their IP addresses, thus allowing them to -+ maintain upper layer connections (e.g. TCP). Selecting this option -+ allows your computer to act as a Correspondent Node (CN). A MIPv6 -+ Mobile Node will be able to communicate with the CN and use route -+ optimization. -+ -+ For more information and configuration details, see -+ http://www.mipl.mediapoli.com/. -+ -+ If unsure, say N. -+ -+MIPv6: Mobile Node Support -+CONFIG_IPV6_MOBILITY_MN -+ If you want your computer to be a MIPv6 Mobile Node (MN), select -+ this option. You must configure MN using the userspace tools -+ available at http://www.mipl.mediapoli.com/download/mipv6-tools/. -+ -+ If your computer is stationary, or you are unsure if you need this, -+ say N. Note that you will need a properly configured MIPv6 Home -+ Agent to use any Mobile Nodes. -+ -+MIPv6: Home Agent Support -+CONFIG_IPV6_MOBILITY_HA -+ If you want your router to serve as a MIPv6 Home Agent (HA), select -+ this option. You must configure HA using the userspace tools -+ available at http://www.mipl.mediapoli.com/download/mipv6-tools/. -+ -+ If your computer is not a router, or you are unsure if you need -+ this, say N. -+ -+MIPv6: Debug messages -+CONFIG_IPV6_MOBILITY_DEBUG -+ MIPL Mobile IPv6 can produce a lot of debugging messages. There are -+ eight debug levels (0 through 7) and the level is controlled via -+ /proc/sys/net/ipv6/mobility/debuglevel. Since MIPL is still -+ experimental, you might want to say Y here. -+ -+ Be sure to say Y and record debug messages when submitting a bug -+ report. - The SCTP Protocol (EXPERIMENTAL) - CONFIG_IP_SCTP - Stream Control Transmission Protocol -diff -uprN linux-2.4.25.old/Documentation/DocBook/Makefile linux-2.4.25/Documentation/DocBook/Makefile ---- linux-2.4.25.old/Documentation/DocBook/Makefile 2002-11-28 23:53:08.000000000 +0000 -+++ linux-2.4.25/Documentation/DocBook/Makefile 2004-06-26 11:29:29.000000000 +0100 -@@ -2,7 +2,7 @@ BOOKS := wanbook.sgml z8530book.sgml mca - kernel-api.sgml parportbook.sgml kernel-hacking.sgml \ - kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \ - deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \ -- journal-api.sgml -+ journal-api.sgml mip6-func.sgml - - PS := $(patsubst %.sgml, %.ps, $(BOOKS)) - PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) -@@ -86,6 +86,9 @@ videobook.sgml: videobook.tmpl $(TOPDIR) - procfs-guide.sgml: procfs-guide.tmpl procfs_example.sgml - $(TOPDIR)/scripts/docgen < procfs-guide.tmpl >$@ - -+mip6-func.sgml: mip6-func.tmpl -+ $(TOPDIR)/scripts/docgen <$< >$@ -+ - APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \ - $(TOPDIR)/arch/i386/kernel/irq.c \ - $(TOPDIR)/arch/i386/kernel/mca.c \ -diff -uprN linux-2.4.25.old/Documentation/DocBook/mip6-func.tmpl linux-2.4.25/Documentation/DocBook/mip6-func.tmpl ---- linux-2.4.25.old/Documentation/DocBook/mip6-func.tmpl 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/Documentation/DocBook/mip6-func.tmpl 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,756 @@ -+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]> -+<book id="LinuxMobileIPv6"> -+ <bookinfo> -+ <title>MIPL Mobile IPv6 Function Reference Guide</title> -+ -+ <authorgroup> -+ <author> -+ <othername>MIPL Mobile IPv6 for Linux Team</othername> -+ <affiliation> -+ <orgname>Helsinki University of Technology</orgname> -+ <orgdiv>Telecommunications Software and Multimedia Lab</orgdiv> -+ <address> -+ <pob>PO BOX 9201</pob> -+ <postcode>FIN-02015 HUT</postcode> -+ <country>Finland</country> -+ <email>mipl@list.mipl.mediapoli.com</email> -+ </address> -+ </affiliation> -+ </author> -+ </authorgroup> -+ -+ <copyright> -+ <year>2000-2001</year> -+ <holder>Helsinki University of Technology</holder> -+ </copyright> -+ -+ <legalnotice> -+ <para> -+ Copyright (c) 2001, 2002 MIPL Mobile IPv6 for Linux Team. -+ </para> -+ <para> -+ Permission is granted to copy, distribute and/or modify this -+ document under the terms of the GNU Free Documentation License, -+ Version 1.1 published by the Free Software Foundation; with the -+ Invariant Sections being "Introduction", with the Front-Cover -+ Texts being "MIPL Mobile IPv6 Function Reference Guide", "MIPL -+ Mobile IPv6 for Linux Team" and "Helsinki University of -+ Technology". A copy of the license is included in <xref -+ linkend="gfdl">. -+ </para> -+ -+ </legalnotice> -+ </bookinfo> -+ -+<toc></toc> -+ -+ <preface id="intro"> -+ <title>Introduction</title> -+ -+ <para> -+ MIPL Mobile IPv6 for Linux is an implementation of Mobility -+ Support in IPv6 IETF mobile-ip working groups Internet-Draft -+ (draft-ietf-mobileip-ipv6). This implementation has been -+ developed in the Telecommunications Software and Multimedia -+ Laboratory at Helsinki University of Technology. -+ </para> -+ -+ <para> -+ MIPL is fully open source, licensed under the GNU General -+ Public License. Latest source for MIPL can be downloaded from -+ the MIPL website at: -+ </para> -+ <programlisting> -+ http://www.mipl.mediapoli.com/. -+ </programlisting> -+ <para> -+ Developers and users interested in MIPL can subscribe to the -+ MIPL mailing list by sending e-mail to -+ <email>majordomo@list.mipl.mediapoli.com</email> with -+ </para> -+ <programlisting> -+ subscribe mipl -+ </programlisting> -+ <para> -+ in the body of the message. -+ </para> -+ -+ <para> -+ This document is a reference guide to MIPL functions. Intended -+ audience is developers wishing to contribute to the project. -+ Hopefully this document will make it easier and quicker to -+ understand and adopt the inner workings of MIPL Mobile IPv6. -+ </para> -+ -+ <para> -+ MIPL Mobile IPv6 for Linux Team members (past and present): -+ -+ <itemizedlist> -+ <listitem> -+ <address> -+ Sami Kivisaari <email>Sami.Kivisaari@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Niklas Kampe <email>Niklas.Kampe@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Juha Mynttinen <email>Juha.Mynttinen@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Toni Nykanen <email>Toni.Nykanen@iki.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Henrik Petander <email>Henrik.Petander@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Antti Tuominen <email>ajtuomin@tml.hut.fi</email> -+ </address> -+ </listitem> -+ </itemizedlist> -+ -+ <itemizedlist> -+ <listitem> -+ <address> -+ Marko Myllynen -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Ville Nuorvala <email>vnuorval@tcs.hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Jaakko Laine <email>Jaakko.Laine@hut.fi</email> -+ </address> -+ </listitem> -+ </itemizedlist> -+ </para> -+ -+ </preface> -+ -+ <chapter id="common"> -+ <title>Common functions for all entities</title> -+ -+ <sect1><title>Low-level functions</title> -+ <para> -+ These functions implement memory allocation used by others. -+ Hashlist functions implement a linked list with hash lookup, -+ which is used with Binding Update List, Binding Cache, Home -+ Agents List etc. -+ </para> -+!Inet/ipv6/mobile_ip6/mempool.h -+!Inet/ipv6/mobile_ip6/hashlist.h -+ </sect1> -+ -+ <sect1><title>Debug functions</title> -+ <para> -+ Debug and utility functions. These functions are available if -+ <constant>CONFIG_IPV6_MOBILITY_DEBUG</constant> is set. -+ Otherwise macros expand to no operation. -+ </para> -+!Inet/ipv6/mobile_ip6/debug.h -+!Inet/ipv6/mobile_ip6/mipv6.c -+ </sect1> -+ -+ <sect1><title>Extension Header functions</title> -+ <para> -+ These functions create and handle extension headers that are -+ specific to MIPv6. -+ </para> -+!Inet/ipv6/mobile_ip6/exthdrs.c -+ </sect1> -+ -+ <sect1><title>Mobility Header functions</title> -+ <para> -+ MIPv6 specifies a new protocol called Mobility Header. -+ Mobility Header has several message types. Messages may also -+ carry Mobility Options. These functions are used to create and -+ handle Mobility Headers and Mobility Options. -+ </para> -+!Inet/ipv6/mobile_ip6/sendopts.c -+!Inet/ipv6/mobile_ip6/mh_recv.c -+!Inet/ipv6/mobile_ip6/auth_subopt.c -+ </sect1> -+ -+ <sect1><title>Binding Cache</title> -+ <para> -+ All Mobile IPv6 entities have a binding cache. These functions -+ provide easy manipulation of the binding cache. -+ </para> -+!Inet/ipv6/mobile_ip6/bcache.c -+ </sect1> -+ -+ <sect1><title>Security</title> -+ -+ <para> -+ These functions are common authentication functions and -+ implement Draft 13 style IPSec AH support for Binding Updates. -+ </para> -+!Inet/ipv6/mobile_ip6/ah_algo.c -+!Inet/ipv6/mobile_ip6/sadb.c -+!Inet/ipv6/mobile_ip6/ah.c -+ </sect1> -+ -+ <sect1><title>Utility functions</title> -+ -+ <para> -+ These functions are general utility functions commonly used by -+ all entities. -+ </para> -+!Inet/ipv6/mobile_ip6/util.c -+ </sect1> -+ -+ </chapter> -+ -+ <chapter id="mn"> -+ <title>Mobile Node functions</title> -+ <sect1><title>General functions</title> -+ <para> -+ </para> -+!Inet/ipv6/mobile_ip6/mn.c -+ </sect1> -+ -+ <sect1><title>Binding Update List</title> -+ <para> -+ Mobile Node keeps track of sent binding updates in Binding -+ Update List. -+ </para> -+!Inet/ipv6/mobile_ip6/bul.c -+ </sect1> -+ -+ <sect1><title>Movement detection</title> -+ -+ <para> -+ These functions are used by the mobile node for movement -+ detection. -+ </para> -+!Inet/ipv6/mobile_ip6/mdetect.c -+ </sect1> -+ </chapter> -+ -+ <chapter id="ha"> -+ <title>Home Agent functions</title> -+ <sect1><title>General functions</title> -+ <para> -+ </para> -+!Inet/ipv6/mobile_ip6/ha.c -+ </sect1> -+ -+ <sect1><title>Duplicate Address Detection functions</title> -+ <para> -+ Home Agent does Duplicate Address Detection for Mobile Nodes' -+ addresses. These functions implement MIPv6 specific DAD -+ functionality. -+ </para> -+!Inet/ipv6/mobile_ip6/dad.c -+ </sect1> -+ -+ </chapter> -+ <appendix id="gfdl"> -+ <title>GNU Free Documentation License</title> -+ -+ <para> -+ Version 1.1, March 2000 -+ </para> -+ -+ <programlisting> -+ Copyright (C) 2000 Free Software Foundation, Inc. -+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ </programlisting> -+ -+ <sect1><title>0. PREAMBLE</title> -+ -+ <para> -+ The purpose of this License is to make a manual, textbook, or -+ other written document "free" in the sense of freedom: to -+ assure everyone the effective freedom to copy and redistribute -+ it, with or without modifying it, either commercially or -+ noncommercially. Secondarily, this License preserves for the -+ author and publisher a way to get credit for their work, while -+ not being considered responsible for modifications made by -+ others. -+ </para> -+ -+ <para> -+ This License is a kind of "copyleft", which means that -+ derivative works of the document must themselves be free in the -+ same sense. It complements the GNU General Public License, -+ which is a copyleft license designed for free software. -+ </para> -+ -+ <para> -+ We have designed this License in order to use it for manuals -+ for free software, because free software needs free -+ documentation: a free program should come with manuals -+ providing the same freedoms that the software does. But this -+ License is not limited to software manuals; it can be used for -+ any textual work, regardless of subject matter or whether it is -+ published as a printed book. We recommend this License -+ principally for works whose purpose is instruction or -+ reference. -+ </para> -+ -+ </sect1> -+ <sect1><title>1. APPLICABILITY AND DEFINITIONS</title> -+ -+ <para> -+ This License applies to any manual or other work that contains -+ a notice placed by the copyright holder saying it can be -+ distributed under the terms of this License. The "Document", -+ below, refers to any such manual or work. Any member of the -+ public is a licensee, and is addressed as "you". -+ </para> -+ -+ <para> -+ A "Modified Version" of the Document means any work containing -+ the Document or a portion of it, either copied verbatim, or -+ with modifications and/or translated into another language. -+ </para> -+ -+ <para> -+ A "Secondary Section" is a named appendix or a front-matter -+ section of the Document that deals exclusively with the -+ relationship of the publishers or authors of the Document to -+ the Document's overall subject (or to related matters) and -+ contains nothing that could fall directly within that overall -+ subject. (For example, if the Document is in part a textbook of -+ mathematics, a Secondary Section may not explain any -+ mathematics.) The relationship could be a matter of historical -+ connection with the subject or with related matters, or of -+ legal, commercial, philosophical, ethical or political position -+ regarding them. -+ </para> -+ -+ <para> -+ The "Invariant Sections" are certain Secondary Sections whose -+ titles are designated, as being those of Invariant Sections, in -+ the notice that says that the Document is released under this -+ License. -+ </para> -+ -+ <para> -+ The "Cover Texts" are certain short passages of text that are -+ listed, as Front-Cover Texts or Back-Cover Texts, in the notice -+ that says that the Document is released under this License. -+ </para> -+ -+ <para> -+ A "Transparent" copy of the Document means a machine-readable -+ copy, represented in a format whose specification is available -+ to the general public, whose contents can be viewed and edited -+ directly and straightforwardly with generic text editors or -+ (for images composed of pixels) generic paint programs or (for -+ drawings) some widely available drawing editor, and that is -+ suitable for input to text formatters or for automatic -+ translation to a variety of formats suitable for input to text -+ formatters. A copy made in an otherwise Transparent file format -+ whose markup has been designed to thwart or discourage -+ subsequent modification by readers is not Transparent. A copy -+ that is not "Transparent" is called "Opaque". -+ </para> -+ -+ <para> -+ Examples of suitable formats for Transparent copies include -+ plain ASCII without markup, Texinfo input format, LaTeX input -+ format, SGML or XML using a publicly available DTD, and -+ standard-conforming simple HTML designed for human -+ modification. Opaque formats include PostScript, PDF, -+ proprietary formats that can be read and edited only by -+ proprietary word processors, SGML or XML for which the DTD -+ and/or processing tools are not generally available, and the -+ machine-generated HTML produced by some word processors for -+ output purposes only. -+ </para> -+ -+ <para> -+ The "Title Page" means, for a printed book, the title page -+ itself, plus such following pages as are needed to hold, -+ legibly, the material this License requires to appear in the -+ title page. For works in formats which do not have any title -+ page as such, "Title Page" means the text near the most -+ prominent appearance of the work's title, preceding the -+ beginning of the body of the text. -+ </para> -+ -+ </sect1> -+ <sect1><title>2. VERBATIM COPYING</title> -+ -+ <para> -+ You may copy and distribute the Document in any medium, either -+ commercially or noncommercially, provided that this License, -+ the copyright notices, and the license notice saying this -+ License applies to the Document are reproduced in all copies, -+ and that you add no other conditions whatsoever to those of -+ this License. You may not use technical measures to obstruct or -+ control the reading or further copying of the copies you make -+ or distribute. However, you may accept compensation in exchange -+ for copies. If you distribute a large enough number of copies -+ you must also follow the conditions in section 3. -+ </para> -+ -+ <para> -+ You may also lend copies, under the same conditions stated -+ above, and you may publicly display copies. -+ </para> -+ -+ </sect1> -+ <sect1><title>3. COPYING IN QUANTITY</title> -+ -+ <para> -+ If you publish printed copies of the Document numbering more -+ than 100, and the Document's license notice requires Cover -+ Texts, you must enclose the copies in covers that carry, -+ clearly and legibly, all these Cover Texts: Front-Cover Texts -+ on the front cover, and Back-Cover Texts on the back -+ cover. Both covers must also clearly and legibly identify you -+ as the publisher of these copies. The front cover must present -+ the full title with all words of the title equally prominent -+ and visible. You may add other material on the covers in -+ addition. Copying with changes limited to the covers, as long -+ as they preserve the title of the Document and satisfy these -+ conditions, can be treated as verbatim copying in other -+ respects. -+ </para> -+ -+ <para> -+ If the required texts for either cover are too voluminous to -+ fit legibly, you should put the first ones listed (as many as -+ fit reasonably) on the actual cover, and continue the rest onto -+ adjacent pages. -+ </para> -+ -+ <para> -+ If you publish or distribute Opaque copies of the Document -+ numbering more than 100, you must either include a -+ machine-readable Transparent copy along with each Opaque copy, -+ or state in or with each Opaque copy a publicly-accessible -+ computer-network location containing a complete Transparent -+ copy of the Document, free of added material, which the general -+ network-using public has access to download anonymously at no -+ charge using public-standard network protocols. If you use the -+ latter option, you must take reasonably prudent steps, when you -+ begin distribution of Opaque copies in quantity, to ensure that -+ this Transparent copy will remain thus accessible at the stated -+ location until at least one year after the last time you -+ distribute an Opaque copy (directly or through your agents or -+ retailers) of that edition to the public. -+ </para> -+ -+ <para> -+ It is requested, but not required, that you contact the authors -+ of the Document well before redistributing any large number of -+ copies, to give them a chance to provide you with an updated -+ version of the Document. -+ </para> -+ -+ </sect1> -+ <sect1><title>4. MODIFICATIONS</title> -+ -+ <para> -+ You may copy and distribute a Modified Version of the Document -+ under the conditions of sections 2 and 3 above, provided that -+ you release the Modified Version under precisely this License, -+ with the Modified Version filling the role of the Document, -+ thus licensing distribution and modification of the Modified -+ Version to whoever possesses a copy of it. In addition, you -+ must do these things in the Modified Version: -+ </para> -+ -+ <para> -+ <itemizedlist spacing=compact> -+ <listitem> -+ <para> -+ A. Use in the Title Page (and on the covers, if any) a title -+ distinct from that of the Document, and from those of previous -+ versions (which should, if there were any, be listed in the -+ History section of the Document). You may use the same title -+ as a previous version if the original publisher of that -+ version gives permission. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ B. List on the Title Page, as authors, one or more persons -+ or entities responsible for authorship of the modifications in -+ the Modified Version, together with at least five of the -+ principal authors of the Document (all of its principal -+ authors, if it has less than five). -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ C. State on the Title page the name of the publisher of the -+ Modified Version, as the publisher. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ D. Preserve all the copyright notices of the Document. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ E. Add an appropriate copyright notice for your -+ modifications adjacent to the other copyright notices. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ F. Include, immediately after the copyright notices, a -+ license notice giving the public permission to use the -+ Modified Version under the terms of this License, in the form -+ shown in the Addendum below. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ G. Preserve in that license notice the full lists of -+ Invariant Sections and required Cover Texts given in the -+ Document's license notice. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ H. Include an unaltered copy of this License. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ I. Preserve the section entitled "History", and its title, -+ and add to it an item stating at least the title, year, new -+ authors, and publisher of the Modified Version as given on the -+ Title Page. If there is no section entitled "History" in the -+ Document, create one stating the title, year, authors, and -+ publisher of the Document as given on its Title Page, then add -+ an item describing the Modified Version as stated in the -+ previous sentence. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ J. Preserve the network location, if any, given in the -+ Document for public access to a Transparent copy of the -+ Document, and likewise the network locations given in the -+ Document for previous versions it was based on. These may be -+ placed in the "History" section. You may omit a network -+ location for a work that was published at least four years -+ before the Document itself, or if the original publisher of -+ the version it refers to gives permission. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ K. In any section entitled "Acknowledgements" or -+ "Dedications", preserve the section's title, and preserve in -+ the section all the substance and tone of each of the -+ contributor acknowledgements and/or dedications given therein. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ L. Preserve all the Invariant Sections of the Document, -+ unaltered in their text and in their titles. Section numbers -+ or the equivalent are not considered part of the section -+ titles. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ M. Delete any section entitled "Endorsements". Such a -+ section may not be included in the Modified Version. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ N. Do not retitle any existing section as "Endorsements" or -+ to conflict in title with any Invariant Section. -+ </para> -+ </listitem> -+ </itemizedlist> -+ </para> -+ -+ <para> -+ If the Modified Version includes new front-matter sections or -+ appendices that qualify as Secondary Sections and contain no -+ material copied from the Document, you may at your option -+ designate some or all of these sections as invariant. To do -+ this, add their titles to the list of Invariant Sections in the -+ Modified Version's license notice. These titles must be -+ distinct from any other section titles. -+ </para> -+ -+ <para> -+ You may add a section entitled "Endorsements", provided it -+ contains nothing but endorsements of your Modified Version by -+ various parties--for example, statements of peer review or that -+ the text has been approved by an organization as the -+ authoritative definition of a standard. -+ </para> -+ -+ <para> -+ You may add a passage of up to five words as a Front-Cover -+ Text, and a passage of up to 25 words as a Back-Cover Text, to -+ the end of the list of Cover Texts in the Modified -+ Version. Only one passage of Front-Cover Text and one of -+ Back-Cover Text may be added by (or through arrangements made -+ by) any one entity. If the Document already includes a cover -+ text for the same cover, previously added by you or by -+ arrangement made by the same entity you are acting on behalf -+ of, you may not add another; but you may replace the old one, -+ on explicit permission from the previous publisher that added -+ the old one. -+ </para> -+ -+ <para> -+ The author(s) and publisher(s) of the Document do not by this -+ License give permission to use their names for publicity for or -+ to assert or imply endorsement of any Modified Version. -+ </para> -+ -+ </sect1> -+ <sect1><title>5. COMBINING DOCUMENTS</title> -+ -+ <para> -+ You may combine the Document with other documents released -+ under this License, under the terms defined in section 4 above -+ for modified versions, provided that you include in the -+ combination all of the Invariant Sections of all of the -+ original documents, unmodified, and list them all as Invariant -+ Sections of your combined work in its license notice. -+ </para> -+ -+ <para> -+ The combined work need only contain one copy of this License, -+ and multiple identical Invariant Sections may be replaced with -+ a single copy. If there are multiple Invariant Sections with -+ the same name but different contents, make the title of each -+ such section unique by adding at the end of it, in parentheses, -+ the name of the original author or publisher of that section if -+ known, or else a unique number. Make the same adjustment to the -+ section titles in the list of Invariant Sections in the license -+ notice of the combined work. -+ </para> -+ -+ <para> -+ In the combination, you must combine any sections entitled -+ "History" in the various original documents, forming one -+ section entitled "History"; likewise combine any sections -+ entitled "Acknowledgements", and any sections entitled -+ "Dedications". You must delete all sections entitled -+ "Endorsements." -+ </para> -+ -+ </sect1> -+ <sect1><title>6. COLLECTIONS OF DOCUMENTS</title> -+ -+ <para> -+ You may make a collection consisting of the Document and other -+ documents released under this License, and replace the -+ individual copies of this License in the various documents with -+ a single copy that is included in the collection, provided that -+ you follow the rules of this License for verbatim copying of -+ each of the documents in all other respects. -+ </para> -+ -+ <para> -+ You may extract a single document from such a collection, and -+ distribute it individually under this License, provided you -+ insert a copy of this License into the extracted document, and -+ follow this License in all other respects regarding verbatim -+ copying of that document. -+ </para> -+ -+ </sect1> -+ <sect1><title>7. AGGREGATION WITH INDEPENDENT WORKS</title> -+ -+ <para> -+ A compilation of the Document or its derivatives with other -+ separate and independent documents or works, in or on a volume -+ of a storage or distribution medium, does not as a whole count -+ as a Modified Version of the Document, provided no compilation -+ copyright is claimed for the compilation. Such a compilation is -+ called an "aggregate", and this License does not apply to the -+ other self-contained works thus compiled with the Document, on -+ account of their being thus compiled, if they are not -+ themselves derivative works of the Document. -+ </para> -+ -+ <para> -+ If the Cover Text requirement of section 3 is applicable to -+ these copies of the Document, then if the Document is less than -+ one quarter of the entire aggregate, the Document's Cover Texts -+ may be placed on covers that surround only the Document within -+ the aggregate. Otherwise they must appear on covers around the -+ whole aggregate. -+ </para> -+ -+ </sect1> -+ <sect1><title>8. TRANSLATION</title> -+ -+ <para> -+ Translation is considered a kind of modification, so you may -+ distribute translations of the Document under the terms of -+ section 4. Replacing Invariant Sections with translations -+ requires special permission from their copyright holders, but -+ you may include translations of some or all Invariant Sections -+ in addition to the original versions of these Invariant -+ Sections. You may include a translation of this License -+ provided that you also include the original English version of -+ this License. In case of a disagreement between the translation -+ and the original English version of this License, the original -+ English version will prevail. -+ </para> -+ -+ </sect1> -+ <sect1><title>9. TERMINATION</title> -+ -+ <para> -+ You may not copy, modify, sublicense, or distribute the -+ Document except as expressly provided for under this -+ License. Any other attempt to copy, modify, sublicense or -+ distribute the Document is void, and will automatically -+ terminate your rights under this License. However, parties who -+ have received copies, or rights, from you under this License -+ will not have their licenses terminated so long as such parties -+ remain in full compliance. -+ </para> -+ -+ </sect1> -+ <sect1><title>10. FUTURE REVISIONS OF THIS LICENSE</title> -+ -+ <para> -+ The Free Software Foundation may publish new, revised versions -+ of the GNU Free Documentation License from time to time. Such -+ new versions will be similar in spirit to the present version, -+ but may differ in detail to address new problems or -+ concerns. See http://www.gnu.org/copyleft/. -+ </para> -+ -+ <para> -+ Each version of the License is given a distinguishing version -+ number. If the Document specifies that a particular numbered -+ version of this License "or any later version" applies to it, -+ you have the option of following the terms and conditions -+ either of that specified version or of any later version that -+ has been published (not as a draft) by the Free Software -+ Foundation. If the Document does not specify a version number -+ of this License, you may choose any version ever published (not -+ as a draft) by the Free Software Foundation. -+ </para> -+ -+ </sect1> -+ </appendix> -+</book> -diff -uprN linux-2.4.25.old/include/linux/icmpv6.h linux-2.4.25/include/linux/icmpv6.h ---- linux-2.4.25.old/include/linux/icmpv6.h 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/include/linux/icmpv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -40,14 +40,16 @@ struct icmp6hdr { - struct icmpv6_nd_ra { - __u8 hop_limit; - #if defined(__LITTLE_ENDIAN_BITFIELD) -- __u8 reserved:6, -+ __u8 reserved:5, -+ home_agent:1, - other:1, - managed:1; - - #elif defined(__BIG_ENDIAN_BITFIELD) - __u8 managed:1, - other:1, -- reserved:6; -+ home_agent:1, -+ reserved:5; - #else - #error "Please fix <asm/byteorder.h>" - #endif -@@ -70,6 +72,7 @@ struct icmp6hdr { - #define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed - #define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other - #define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime -+#define icmp6_home_agent icmp6_dataun.u_nd_ra.home_agent - }; - - -diff -uprN linux-2.4.25.old/include/linux/if_arp.h linux-2.4.25/include/linux/if_arp.h ---- linux-2.4.25.old/include/linux/if_arp.h 2002-02-25 19:38:13.000000000 +0000 -+++ linux-2.4.25/include/linux/if_arp.h 2004-06-26 11:29:29.000000000 +0100 -@@ -59,7 +59,7 @@ - #define ARPHRD_RAWHDLC 518 /* Raw HDLC */ - - #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ --#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */ -+#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ - #define ARPHRD_FRAD 770 /* Frame Relay Access Device */ - #define ARPHRD_SKIP 771 /* SKIP vif */ - #define ARPHRD_LOOPBACK 772 /* Loopback device */ -diff -uprN linux-2.4.25.old/include/linux/in6.h linux-2.4.25/include/linux/in6.h ---- linux-2.4.25.old/include/linux/in6.h 2003-06-13 15:51:38.000000000 +0100 -+++ linux-2.4.25/include/linux/in6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -142,6 +142,11 @@ struct in6_flowlabel_req - #define IPV6_TLV_JUMBO 194 - - /* -+ * Mobile IPv6 TLV options. -+ */ -+#define MIPV6_TLV_HOMEADDR 201 -+ -+/* - * IPV6 socket options - */ - -diff -uprN linux-2.4.25.old/include/linux/ipv6.h linux-2.4.25/include/linux/ipv6.h ---- linux-2.4.25.old/include/linux/ipv6.h 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/include/linux/ipv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -29,6 +29,7 @@ struct in6_ifreq { - - #define IPV6_SRCRT_STRICT 0x01 /* this hop must be a neighbor */ - #define IPV6_SRCRT_TYPE_0 0 /* IPv6 type 0 Routing Header */ -+#define IPV6_SRCRT_TYPE_2 2 /* type 2 for Mobile IPv6 */ - - /* - * routing header -@@ -71,6 +72,19 @@ struct rt0_hdr { - struct in6_addr addr[0]; - - #define rt0_type rt_hdr.type -+ -+}; -+ -+/* -+ * routing header type 2 -+ */ -+ -+struct rt2_hdr { -+ struct ipv6_rt_hdr rt_hdr; -+ __u32 reserved; -+ struct in6_addr addr; -+ -+#define rt2_type rt_hdr.type; - }; - - /* -@@ -156,12 +170,16 @@ enum { - struct inet6_skb_parm - { - int iif; -+ __u8 mipv6_flags; - __u16 ra; - __u16 hop; - __u16 auth; - __u16 dst0; - __u16 srcrt; -+ __u16 srcrt2; -+ __u16 hao; - __u16 dst1; -+ struct in6_addr hoa; - }; - - #endif -diff -uprN linux-2.4.25.old/include/linux/ipv6_route.h linux-2.4.25/include/linux/ipv6_route.h ---- linux-2.4.25.old/include/linux/ipv6_route.h 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/include/linux/ipv6_route.h 2004-06-26 11:29:29.000000000 +0100 -@@ -33,6 +33,7 @@ enum - #define RTF_CACHE 0x01000000 /* cache entry */ - #define RTF_FLOW 0x02000000 /* flow significant route */ - #define RTF_POLICY 0x04000000 /* policy route */ -+#define RTF_MOBILENODE 0x10000000 /* for routing to Mobile Node */ - - #define RTF_LOCAL 0x80000000 - -diff -uprN linux-2.4.25.old/include/linux/ipv6_tunnel.h linux-2.4.25/include/linux/ipv6_tunnel.h ---- linux-2.4.25.old/include/linux/ipv6_tunnel.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/linux/ipv6_tunnel.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,34 @@ -+/* -+ * $Id$ -+ */ -+ -+#ifndef _IPV6_TUNNEL_H -+#define _IPV6_TUNNEL_H -+ -+#define IPV6_TLV_TNL_ENCAP_LIMIT 4 -+#define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4 -+ -+/* don't add encapsulation limit if one isn't present in inner packet */ -+#define IP6_TNL_F_IGN_ENCAP_LIMIT 0x1 -+/* copy the traffic class field from the inner packet */ -+#define IP6_TNL_F_USE_ORIG_TCLASS 0x2 -+/* copy the flowlabel from the inner packet */ -+#define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4 -+/* created and maintained from within the kernel */ -+#define IP6_TNL_F_KERNEL_DEV 0x8 -+/* being used for Mobile IPv6 */ -+#define IP6_TNL_F_MIP6_DEV 0x10 -+ -+struct ip6_tnl_parm { -+ char name[IFNAMSIZ]; /* name of tunnel device */ -+ int link; /* ifindex of underlying L2 interface */ -+ __u8 proto; /* tunnel protocol */ -+ __u8 encap_limit; /* encapsulation limit for tunnel */ -+ __u8 hop_limit; /* hop limit for tunnel */ -+ __u32 flowinfo; /* traffic class and flowlabel for tunnel */ -+ __u32 flags; /* tunnel flags */ -+ struct in6_addr laddr; /* local tunnel end-point address */ -+ struct in6_addr raddr; /* remote tunnel end-point address */ -+}; -+ -+#endif -diff -uprN linux-2.4.25.old/include/linux/rtnetlink.h linux-2.4.25/include/linux/rtnetlink.h ---- linux-2.4.25.old/include/linux/rtnetlink.h 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/include/linux/rtnetlink.h 2004-06-26 11:29:29.000000000 +0100 -@@ -309,15 +309,17 @@ enum - IFA_LABEL, - IFA_BROADCAST, - IFA_ANYCAST, -- IFA_CACHEINFO -+ IFA_CACHEINFO, -+ IFA_HOMEAGENT - }; - --#define IFA_MAX IFA_CACHEINFO -+#define IFA_MAX IFA_HOMEAGENT - - /* ifa_flags */ - - #define IFA_F_SECONDARY 0x01 - -+#define IFA_F_HOMEADDR 0x10 - #define IFA_F_DEPRECATED 0x20 - #define IFA_F_TENTATIVE 0x40 - #define IFA_F_PERMANENT 0x80 -diff -uprN linux-2.4.25.old/include/linux/skbuff.h linux-2.4.25/include/linux/skbuff.h ---- linux-2.4.25.old/include/linux/skbuff.h 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/include/linux/skbuff.h 2004-06-26 11:29:29.000000000 +0100 -@@ -177,7 +177,7 @@ struct sk_buff { - * want to keep them across layers you have to do a skb_clone() - * first. This is owned by whoever has the skb queued ATM. - */ -- char cb[48]; -+ char cb[64]; - - unsigned int len; /* Length of actual data */ - unsigned int data_len; -diff -uprN linux-2.4.25.old/include/linux/sysctl.h linux-2.4.25/include/linux/sysctl.h ---- linux-2.4.25.old/include/linux/sysctl.h 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/include/linux/sysctl.h 2004-06-26 11:29:29.000000000 +0100 -@@ -387,7 +387,24 @@ enum { - NET_IPV6_NEIGH=17, - NET_IPV6_ROUTE=18, - NET_IPV6_ICMP=19, -- NET_IPV6_BINDV6ONLY=20 -+ NET_IPV6_BINDV6ONLY=20, -+ NET_IPV6_MOBILITY=26 -+}; -+ -+/* /proc/sys/net/ipv6/mobility */ -+enum { -+ NET_IPV6_MOBILITY_DEBUG=1, -+ NET_IPV6_MOBILITY_TUNNEL_SITELOCAL=2, -+ NET_IPV6_MOBILITY_ROUTER_SOLICITATION_MAX_SENDTIME=3, -+ NET_IPV6_MOBILITY_ROUTER_REACH=4, -+ NET_IPV6_MOBILITY_MDETECT_MECHANISM=5, -+ NET_IPV6_MOBILITY_RETROUT=6, -+ NET_IPV6_MOBILITY_MAX_TNLS=7, -+ NET_IPV6_MOBILITY_MIN_TNLS=8, -+ NET_IPV6_MOBILITY_BINDING_REFRESH=9, -+ NET_IPV6_MOBILITY_BU_F_LLADDR=10, -+ NET_IPV6_MOBILITY_BU_F_KEYMGM=11, -+ NET_IPV6_MOBILITY_BU_F_CN_ACK=12 - }; - - enum { -diff -uprN linux-2.4.25.old/include/net/addrconf.h linux-2.4.25/include/net/addrconf.h ---- linux-2.4.25.old/include/net/addrconf.h 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/include/net/addrconf.h 2004-06-26 11:29:29.000000000 +0100 -@@ -16,9 +16,11 @@ struct prefix_info { - #if defined(__BIG_ENDIAN_BITFIELD) - __u8 onlink : 1, - autoconf : 1, -- reserved : 6; -+ router_address : 1, -+ reserved : 5; - #elif defined(__LITTLE_ENDIAN_BITFIELD) -- __u8 reserved : 6, -+ __u8 reserved : 5, -+ router_address : 1, - autoconf : 1, - onlink : 1; - #else -@@ -55,6 +57,7 @@ extern int ipv6_chk_addr(struct in6_ad - struct net_device *dev); - extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, - struct net_device *dev); -+extern void ipv6_del_addr(struct inet6_ifaddr *ifp); - extern int ipv6_get_saddr(struct dst_entry *dst, - struct in6_addr *daddr, - struct in6_addr *saddr); -@@ -85,7 +88,9 @@ extern void ipv6_mc_up(struct inet6_dev - extern void ipv6_mc_down(struct inet6_dev *idev); - extern void ipv6_mc_init_dev(struct inet6_dev *idev); - extern void ipv6_mc_destroy_dev(struct inet6_dev *idev); -+extern void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); - extern void addrconf_dad_failure(struct inet6_ifaddr *ifp); -+extern void addrconf_dad_completed(struct inet6_ifaddr *ifp); - - extern int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group, - struct in6_addr *src_addr); -@@ -116,6 +121,9 @@ extern int ipv6_chk_acast_addr(struct - extern int register_inet6addr_notifier(struct notifier_block *nb); - extern int unregister_inet6addr_notifier(struct notifier_block *nb); - -+extern int ipv6_generate_eui64(u8 *eui, struct net_device *dev); -+extern int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev); -+ - static inline struct inet6_dev * - __in6_dev_get(struct net_device *dev) - { -diff -uprN linux-2.4.25.old/include/net/ip6_route.h linux-2.4.25/include/net/ip6_route.h ---- linux-2.4.25.old/include/net/ip6_route.h 2003-06-13 15:51:39.000000000 +0100 -+++ linux-2.4.25/include/net/ip6_route.h 2004-06-26 11:29:29.000000000 +0100 -@@ -2,6 +2,7 @@ - #define _NET_IP6_ROUTE_H - - #define IP6_RT_PRIO_FW 16 -+#define IP6_RT_PRIO_MIPV6 64 - #define IP6_RT_PRIO_USER 1024 - #define IP6_RT_PRIO_ADDRCONF 256 - #define IP6_RT_PRIO_KERN 512 -@@ -40,6 +41,9 @@ extern int ipv6_route_ioctl(unsigned i - - extern int ip6_route_add(struct in6_rtmsg *rtmsg, - struct nlmsghdr *); -+ -+extern int ip6_route_del(struct in6_rtmsg *rtmsg, -+ struct nlmsghdr *); - extern int ip6_del_rt(struct rt6_info *, - struct nlmsghdr *); - -@@ -99,7 +103,8 @@ extern rwlock_t rt6_lock; - */ - - static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, -- struct in6_addr *daddr) -+ struct in6_addr *daddr, -+ struct in6_addr *saddr) - { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - struct rt6_info *rt = (struct rt6_info *) dst; -@@ -107,6 +112,9 @@ static inline void ip6_dst_store(struct - write_lock(&sk->dst_lock); - __sk_dst_set(sk, dst); - np->daddr_cache = daddr; -+#ifdef CONFIG_IPV6_SUBTREES -+ np->saddr_cache = saddr; -+#endif - np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; - write_unlock(&sk->dst_lock); - } -diff -uprN linux-2.4.25.old/include/net/ipv6.h linux-2.4.25/include/net/ipv6.h ---- linux-2.4.25.old/include/net/ipv6.h 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/include/net/ipv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -37,6 +37,7 @@ - #define NEXTHDR_ICMP 58 /* ICMP for IPv6. */ - #define NEXTHDR_NONE 59 /* No next header */ - #define NEXTHDR_DEST 60 /* Destination options header. */ -+#define NEXTHDR_MH 135 /* Mobility header, RFC 3775 */ - - #define NEXTHDR_MAX 255 - -@@ -145,9 +146,12 @@ struct ipv6_txoptions - __u16 opt_flen; /* after fragment hdr */ - __u16 opt_nflen; /* before fragment hdr */ - -+ __u8 mipv6_flags; /* flags set by MIPv6 */ -+ - struct ipv6_opt_hdr *hopopt; - struct ipv6_opt_hdr *dst0opt; -- struct ipv6_rt_hdr *srcrt; /* Routing Header */ -+ struct ipv6_rt_hdr *srcrt; /* Routing Header Type 0 */ -+ struct ipv6_rt_hdr *srcrt2; /* Routing Header Type 2 */ - struct ipv6_opt_hdr *auth; - struct ipv6_opt_hdr *dst1opt; - -@@ -256,6 +260,38 @@ static inline int ipv6_addr_any(const st - a->s6_addr32[2] | a->s6_addr32[3] ) == 0); - } - -+static inline void ipv6_addr_prefix(struct in6_addr *pfx, -+ const struct in6_addr *addr, int plen) -+{ -+ /* caller must guarantee 0 <= plen <= 128 */ -+ int o = plen >> 3, -+ b = plen & 0x7; -+ -+ memcpy(pfx->s6_addr, addr, o); -+ if (b != 0) { -+ pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b); -+ o++; -+ } -+ if (o < 16) -+ memset(pfx->s6_addr + o, 0, 16 - o); -+} -+ -+static inline int ipv6_prefix_cmp(const struct in6_addr *p1, -+ const struct in6_addr *p2, int plen) -+{ -+ int b = plen&0x7; -+ int o = plen>>3; -+ int res = 0; -+ -+ if (o > 0) -+ res = memcmp(&p1->s6_addr[0], &p2->s6_addr[0], o); -+ if (res == 0 && b > 0) { -+ __u8 m = (0xff00 >> b) & 0xff; -+ res = (p1->s6_addr[o] & m) - (p2->s6_addr[o] & m); -+ } -+ return res; -+} -+ - /* - * Prototypes exported by ipv6 - */ -diff -uprN linux-2.4.25.old/include/net/ipv6_tunnel.h linux-2.4.25/include/net/ipv6_tunnel.h ---- linux-2.4.25.old/include/net/ipv6_tunnel.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/net/ipv6_tunnel.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,92 @@ -+/* -+ * $Id$ -+ */ -+ -+#ifndef _NET_IPV6_TUNNEL_H -+#define _NET_IPV6_TUNNEL_H -+ -+#include <linux/ipv6.h> -+#include <linux/netdevice.h> -+#include <linux/ipv6_tunnel.h> -+#include <linux/skbuff.h> -+#include <asm/atomic.h> -+ -+/* capable of sending packets */ -+#define IP6_TNL_F_CAP_XMIT 0x10000 -+/* capable of receiving packets */ -+#define IP6_TNL_F_CAP_RCV 0x20000 -+ -+#define IP6_TNL_MAX 128 -+ -+/* IPv6 tunnel */ -+ -+struct ip6_tnl { -+ struct ip6_tnl *next; /* next tunnel in list */ -+ struct net_device *dev; /* virtual device associated with tunnel */ -+ struct net_device_stats stat; /* statistics for tunnel device */ -+ int recursion; /* depth of hard_start_xmit recursion */ -+ struct ip6_tnl_parm parms; /* tunnel configuration paramters */ -+ struct flowi fl; /* flowi template for xmit */ -+ atomic_t refcnt; /* nr of identical tunnels used by kernel */ -+ struct socket *sock; -+}; -+ -+#define IP6_TNL_PRE_ENCAP 0 -+#define IP6_TNL_PRE_DECAP 1 -+#define IP6_TNL_MAXHOOKS 2 -+ -+#define IP6_TNL_DROP 0 -+#define IP6_TNL_ACCEPT 1 -+ -+typedef int ip6_tnl_hookfn(struct ip6_tnl *t, struct sk_buff *skb); -+ -+struct ip6_tnl_hook_ops { -+ struct list_head list; -+ unsigned int hooknum; -+ int priority; -+ ip6_tnl_hookfn *hook; -+}; -+ -+enum ip6_tnl_hook_priorities { -+ IP6_TNL_PRI_FIRST = INT_MIN, -+ IP6_TNL_PRI_LAST = INT_MAX -+}; -+ -+/* Tunnel encapsulation limit destination sub-option */ -+ -+struct ipv6_tlv_tnl_enc_lim { -+ __u8 type; /* type-code for option */ -+ __u8 length; /* option length */ -+ __u8 encap_limit; /* tunnel encapsulation limit */ -+} __attribute__ ((packed)); -+ -+#ifdef __KERNEL__ -+extern int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt); -+ -+extern struct ip6_tnl *ip6ip6_tnl_lookup(struct in6_addr *remote, -+ struct in6_addr *local); -+ -+void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p); -+ -+extern int ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p); -+ -+extern int ip6ip6_kernel_tnl_del(struct ip6_tnl *t); -+ -+extern unsigned int ip6ip6_tnl_inc_max_kdev_count(unsigned int n); -+ -+extern unsigned int ip6ip6_tnl_dec_max_kdev_count(unsigned int n); -+ -+extern unsigned int ip6ip6_tnl_inc_min_kdev_count(unsigned int n); -+ -+extern unsigned int ip6ip6_tnl_dec_min_kdev_count(unsigned int n); -+ -+extern void ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg); -+ -+extern void ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg); -+ -+#ifdef CONFIG_IPV6_TUNNEL -+extern int __init ip6_tunnel_init(void); -+extern void ip6_tunnel_cleanup(void); -+#endif -+#endif -+#endif -diff -uprN linux-2.4.25.old/include/net/mipglue.h linux-2.4.25/include/net/mipglue.h ---- linux-2.4.25.old/include/net/mipglue.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/net/mipglue.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,266 @@ -+/* -+ * Glue for Mobility support integration to IPv6 -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#ifndef _NET_MIPGLUE_H -+#define _NET_MIPGLUE_H -+ -+#ifndef USE_IPV6_MOBILITY -+#if defined(CONFIG_IPV6_MOBILITY) || defined(CONFIG_IPV6_MOBILITY_MODULE) -+#define USE_IPV6_MOBILITY -+#endif -+#endif -+ -+/* symbols to indicate whether destination options received should take -+ * effect or not (see exthdrs.c, procrcv.c) -+ */ -+#define MIPV6_DSTOPTS_ACCEPT 1 -+#define MIPV6_DSTOPTS_DISCARD 0 -+ -+#define MIPV6_IGN_RTR 0 -+#define MIPV6_ADD_RTR 1 -+#define MIPV6_CHG_RTR 2 -+ -+/* MIPV6: Approximate maximum for mobile IPv6 options and headers */ -+#define MIPV6_HEADERS 48 -+ -+#ifdef __KERNEL__ -+#include <net/mipv6.h> -+#include <linux/slab.h> -+#include <net/ipv6.h> -+ -+struct sk_buff; -+struct ndisc_options; -+struct sock; -+struct ipv6_txoptions; -+struct flowi; -+struct dst_entry; -+struct in6_addr; -+struct inet6_ifaddr; -+ -+#ifdef USE_IPV6_MOBILITY -+ -+/* calls a procedure from mipv6-module */ -+#define MIPV6_CALLPROC(X) if(mipv6_functions.X) mipv6_functions.X -+ -+/* calls a function from mipv6-module, default-value if function not defined -+ */ -+#define MIPV6_CALLFUNC(X,Y) (!mipv6_functions.X)?(Y):mipv6_functions.X -+ -+/* sets a handler-function to process a call */ -+#define MIPV6_SETCALL(X,Y) if(mipv6_functions.X) printk("mipv6: Warning, function assigned twice!\n"); \ -+ mipv6_functions.X = Y -+#define MIPV6_RESETCALL(X) mipv6_functions.X = NULL -+ -+/* pointers to mipv6 callable functions */ -+struct mipv6_callable_functions { -+ void (*mipv6_initialize_dstopt_rcv) (struct sk_buff *skb); -+ int (*mipv6_finalize_dstopt_rcv) (int process); -+ int (*mipv6_handle_homeaddr) (struct sk_buff *skb, int optoff); -+ int (*mipv6_ra_rcv) (struct sk_buff *skb, -+ struct ndisc_options *ndopts); -+ void (*mipv6_icmp_rcv) (struct sk_buff *skb); -+ struct ipv6_txoptions * (*mipv6_modify_txoptions) ( -+ struct sock *sk, -+ struct sk_buff *skb, -+ struct ipv6_txoptions *opt, -+ struct flowi *fl, -+ struct dst_entry **dst); -+ void (*mipv6_set_home) (int ifindex, struct in6_addr *homeaddr, -+ int plen, struct in6_addr *homeagent, -+ int plen2); -+ void (*mipv6_get_home_address) (struct in6_addr *home_addr); -+ void (*mipv6_get_care_of_address)(struct in6_addr *homeaddr, -+ struct in6_addr *coa); -+ int (*mipv6_is_home_addr)(struct in6_addr *addr); -+ void (*mipv6_change_router)(void); -+ void (*mipv6_check_dad)(struct in6_addr *home_addr); -+ void (*mipv6_icmp_swap_addrs)(struct sk_buff *skb); -+ int (*mipv6_forward)(struct sk_buff *skb); -+ int (*mipv6_mn_ha_probe)(struct inet6_ifaddr *ifp, u8 *lladdr); -+}; -+ -+extern struct mipv6_callable_functions mipv6_functions; -+ -+extern void mipv6_invalidate_calls(void); -+ -+extern int mipv6_handle_dstopt(struct sk_buff *skb, int optoff); -+ -+static inline int -+ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) -+{ -+ return MIPV6_CALLFUNC(mipv6_mn_ha_probe, 0)(ifp, lladdr); -+} -+ -+/* Must only be called for HA, no checks here */ -+static inline int ip6_mipv6_forward(struct sk_buff *skb) -+{ -+ return MIPV6_CALLFUNC(mipv6_forward, 0)(skb); -+} -+ -+/* -+ * Avoid adding new default routers if the old one is still in use -+ */ -+ -+static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb, -+ struct ndisc_options *ndopts) -+{ -+ return MIPV6_CALLFUNC(mipv6_ra_rcv, MIPV6_ADD_RTR)(skb, ndopts); -+} -+ -+static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr) -+{ -+ return MIPV6_CALLFUNC(mipv6_is_home_addr, 0)(addr); -+} -+ -+static inline void ndisc_mipv6_change_router(int change_rtr) -+{ -+ if (change_rtr == MIPV6_CHG_RTR) -+ MIPV6_CALLPROC(mipv6_change_router)(); -+} -+ -+static inline void ndisc_check_mipv6_dad(struct in6_addr *target) -+{ -+ MIPV6_CALLPROC(mipv6_check_dad)(target); -+} -+ -+static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb) -+{ -+ MIPV6_CALLPROC(mipv6_icmp_swap_addrs)(skb); -+} -+ -+static inline void mipv6_icmp_rcv(struct sk_buff *skb) -+{ -+ MIPV6_CALLPROC(mipv6_icmp_rcv)(skb); -+} -+ -+static inline int tcp_v6_get_mipv6_header_len(void) -+{ -+ return MIPV6_HEADERS; -+} -+ -+static inline struct in6_addr * -+mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr) -+{ -+ return daddr; -+} -+ -+static inline void -+addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen, -+ struct in6_addr *homeagent, int plen2) -+{ -+ MIPV6_CALLPROC(mipv6_set_home)(ifindex, homeaddr, plen, homeagent, plen2); -+} -+ -+static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr) -+{ -+ MIPV6_CALLPROC(mipv6_get_home_address)(saddr); -+} -+ -+static inline struct ipv6_txoptions * -+ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *opt, struct flowi *fl, -+ struct dst_entry **dst) -+{ -+ return MIPV6_CALLFUNC(mipv6_modify_txoptions, opt)(sk, skb, opt, fl, dst); -+ -+} -+ -+static inline void -+ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb) -+{ -+ struct inet6_skb_parm *opt; -+ if (txopt) { -+ opt = (struct inet6_skb_parm *)skb->cb; -+ opt->mipv6_flags = txopt->mipv6_flags; -+ } -+} -+ -+static inline void -+ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt, -+ struct ipv6_txoptions *orig_opt) -+{ -+ if (opt && opt != orig_opt) -+ kfree(opt); -+} -+ -+#else /* USE_IPV6_MOBILITY */ -+ -+#define mipv6_handle_dstopt ip6_tlvopt_unknown -+ -+static inline int -+ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) -+{ -+ return 0; -+} -+ -+static inline int ip6_mipv6_forward(struct sk_buff *skb) -+{ -+ return 0; -+} -+ -+static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb, -+ struct ndisc_options *ndopts) -+{ -+ return MIPV6_ADD_RTR; -+} -+ -+static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr) -+{ -+ return 0; -+} -+ -+static inline void ndisc_mipv6_change_router(int change_rtr) {} -+ -+static inline void ndisc_check_mipv6_dad(struct in6_addr *target) {} -+ -+static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb) {} -+ -+static inline void mipv6_icmp_rcv(struct sk_buff *skb) {} -+ -+static inline int tcp_v6_get_mipv6_header_len(void) -+{ -+ return 0; -+} -+ -+static inline struct in6_addr * -+mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr) -+{ -+ return hdaddr; -+} -+ -+static inline void -+addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen, -+ struct in6_addr *homeagent, int plen2) {} -+ -+static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr) {} -+ -+static inline struct ipv6_txoptions * -+ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *opt, struct flowi *fl, -+ struct dst_entry **dst) -+{ -+ return opt; -+} -+ -+static inline void -+ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb) {} -+ -+static inline void -+ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt, -+ struct ipv6_txoptions *orig_opt) {} -+ -+#endif /* USE_IPV6_MOBILITY */ -+#endif /* __KERNEL__ */ -+#endif /* _NET_MIPGLUE_H */ -diff -uprN linux-2.4.25.old/include/net/mipv6.h linux-2.4.25/include/net/mipv6.h ---- linux-2.4.25.old/include/net/mipv6.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/include/net/mipv6.h 2004-06-26 11:29:29.000000000 +0100 -@@ -0,0 +1,258 @@ -+/* -+ * Mobile IPv6 header-file -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#ifndef _NET_MIPV6_H -+#define _NET_MIPV6_H -+ -+#include <linux/types.h> -+#include <asm/byteorder.h> -+#include <linux/in6.h> -+ -+/* -+ * -+ * Mobile IPv6 Protocol constants -+ * -+ */ -+#define DHAAD_RETRIES 4 /* transmissions */ -+#define INITIAL_BINDACK_TIMEOUT 1 /* seconds */ -+#define INITIAL_DHAAD_TIMEOUT 3 /* seconds */ -+#define INITIAL_SOLICIT_TIMER 3 /* seconds */ -+#define MAX_BINDACK_TIMEOUT 32 /* seconds */ -+#define MAX_NONCE_LIFE 240 /* seconds */ -+#define MAX_TOKEN_LIFE 210 /* seconds */ -+#define MAX_RR_BINDING_LIFE 420 /* seconds */ -+#define MAX_UPDATE_RATE 3 /* 1/s (min delay=1s) */ -+#define PREFIX_ADV_RETRIES 3 /* transmissions */ -+#define PREFIX_ADV_TIMEOUT 3 /* seconds */ -+ -+#define MAX_FAST_UPDATES 5 /* transmissions */ -+#define MAX_PFX_ADV_DELAY 1000 /* seconds */ -+#define SLOW_UPDATE_RATE 10 /* 1/10s (max delay=10s)*/ -+#define INITIAL_BINDACK_DAD_TIMEOUT 2 /* seconds */ -+ -+/* -+ * -+ * Mobile IPv6 (RFC 3775) Protocol configuration variable defaults -+ * -+ */ -+#define DefHomeRtrAdvInterval 1000 /* seconds */ -+#define DefMaxMobPfxAdvInterval 86400 /* seconds */ -+#define DefMinDelayBetweenRAs 3 /* seconds (min 0.03) */ -+#define DefMinMobPfxAdvInterval 600 /* seconds */ -+#define DefInitialBindackTimeoutFirstReg 1.5 /* seconds */ -+ -+/* This is not actually specified in the draft, but is needed to avoid -+ * prefix solicitation storm when valid lifetime of a prefix is smaller -+ * than MAX_PFX_ADV_DELAY -+ */ -+#define MIN_PFX_SOL_DELAY 5 /* seconds */ -+ -+/* Mobile IPv6 ICMP types */ -+/* -+ * Official numbers from RFC 3775 -+ */ -+#define MIPV6_DHAAD_REQUEST 144 -+#define MIPV6_DHAAD_REPLY 145 -+#define MIPV6_PREFIX_SOLICIT 146 -+#define MIPV6_PREFIX_ADV 147 -+ -+/* Binding update flag codes */ -+#define MIPV6_BU_F_ACK 0x80 -+#define MIPV6_BU_F_HOME 0x40 -+#define MIPV6_BU_F_LLADDR 0x20 -+#define MIPV6_BU_F_KEYMGM 0x10 -+ -+/* Binding ackknowledgment flag codes */ -+#define MIPV6_BA_F_KEYMGM 0x80 -+ -+/* Binding error status */ -+#define MIPV6_BE_HAO_WO_BINDING 1 -+#define MIPV6_BE_UNKNOWN_MH_TYPE 2 -+ -+/* Mobility Header */ -+struct mipv6_mh -+{ -+ __u8 payload; /* Payload Protocol */ -+ __u8 length; /* MH Length */ -+ __u8 type; /* MH Type */ -+ __u8 reserved; /* Reserved */ -+ __u16 checksum; /* Checksum */ -+ __u8 data[0]; /* Message specific data */ -+} __attribute__ ((packed)); -+ -+/* Mobility Header type */ -+#define IPPROTO_MOBILITY 135 /* RFC 3775*/ -+/* Mobility Header Message Types */ -+ -+#define MIPV6_MH_BRR 0 -+#define MIPV6_MH_HOTI 1 -+#define MIPV6_MH_COTI 2 -+#define MIPV6_MH_HOT 3 -+#define MIPV6_MH_COT 4 -+#define MIPV6_MH_BU 5 -+#define MIPV6_MH_BA 6 -+#define MIPV6_MH_BE 7 -+ -+/* -+ * Status codes for Binding Acknowledgements -+ */ -+#define SUCCESS 0 -+#define REASON_UNSPECIFIED 128 -+#define ADMINISTRATIVELY_PROHIBITED 129 -+#define INSUFFICIENT_RESOURCES 130 -+#define HOME_REGISTRATION_NOT_SUPPORTED 131 -+#define NOT_HOME_SUBNET 132 -+#define NOT_HA_FOR_MN 133 -+#define DUPLICATE_ADDR_DETECT_FAIL 134 -+#define SEQUENCE_NUMBER_OUT_OF_WINDOW 135 -+#define EXPIRED_HOME_NONCE_INDEX 136 -+#define EXPIRED_CAREOF_NONCE_INDEX 137 -+#define EXPIRED_NONCES 138 -+#define REG_TYPE_CHANGE_FORBIDDEN 139 -+/* -+ * Values for mipv6_flags in struct inet6_skb_parm -+ */ -+ -+#define MIPV6_RCV_TUNNEL 0x1 -+#define MIPV6_SND_HAO 0x2 -+#define MIPV6_SND_BU 0x4 -+ -+/* -+ * Mobility Header Message structures -+ */ -+ -+struct mipv6_mh_brr -+{ -+ __u16 reserved; -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_bu -+{ -+ __u16 sequence; /* sequence number of BU */ -+ __u8 flags; /* flags */ -+ __u8 reserved; /* reserved bits */ -+ __u16 lifetime; /* lifetime of BU */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_ba -+{ -+ __u8 status; /* statuscode */ -+ __u8 reserved; /* reserved bits */ -+ __u16 sequence; /* sequence number of BA */ -+ __u16 lifetime; /* lifetime in CN's bcache */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_be -+{ -+ __u8 status; -+ __u8 reserved; -+ struct in6_addr home_addr; -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_addr_ti -+{ -+ __u16 reserved; /* Reserved */ -+ u_int8_t init_cookie[8]; /* HoT/CoT Init Cookie */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_addr_test -+{ -+ __u16 nonce_index; /* Home/Care-of Nonce Index */ -+ u_int8_t init_cookie[8]; /* HoT/CoT Init Cookie */ -+ u_int8_t kgen_token[8]; /* Home/Care-of key generation token */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+/* -+ * Mobility Options for various MH types. -+ */ -+#define MIPV6_OPT_PAD1 0x00 -+#define MIPV6_OPT_PADN 0x01 -+#define MIPV6_OPT_BIND_REFRESH_ADVICE 0x02 -+#define MIPV6_OPT_ALTERNATE_COA 0x03 -+#define MIPV6_OPT_NONCE_INDICES 0x04 -+#define MIPV6_OPT_AUTH_DATA 0x05 -+ -+#define MIPV6_SEQ_GT(x,y) \ -+ ((short int)(((__u16)(x)) - ((__u16)(y))) > 0) -+ -+/* -+ * Mobility Option structures -+ */ -+ -+struct mipv6_mo -+{ -+ __u8 type; -+ __u8 length; -+ __u8 value[0]; /* type specific data */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_pad1 -+{ -+ __u8 type; -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_padn -+{ -+ __u8 type; -+ __u8 length; -+ __u8 data[0]; -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_alt_coa -+{ -+ __u8 type; -+ __u8 length; -+ struct in6_addr addr; /* alternate care-of-address */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_nonce_indices -+{ -+ __u8 type; -+ __u8 length; -+ __u16 home_nonce_i; /* Home Nonce Index */ -+ __u16 careof_nonce_i; /* Careof Nonce Index */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_bauth_data -+{ -+ __u8 type; -+ __u8 length; -+ __u8 data[0]; -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_br_advice -+{ -+ __u8 type; -+ __u8 length; -+ __u16 refresh_interval; /* Refresh Interval */ -+} __attribute__ ((packed)); -+ -+/* -+ * Home Address Destination Option structure -+ */ -+struct mipv6_dstopt_homeaddr -+{ -+ __u8 type; /* type-code for option */ -+ __u8 length; /* option length */ -+ struct in6_addr addr; /* home address */ -+} __attribute__ ((packed)); -+ -+#endif /* _NET_MIPV6_H */ -diff -uprN linux-2.4.25.old/include/net/ndisc.h linux-2.4.25/include/net/ndisc.h ---- linux-2.4.25.old/include/net/ndisc.h 2002-11-28 23:53:15.000000000 +0000 -+++ linux-2.4.25/include/net/ndisc.h 2004-06-26 11:29:29.000000000 +0100 -@@ -21,6 +21,10 @@ - #define ND_OPT_REDIRECT_HDR 4 - #define ND_OPT_MTU 5 - -+/* Mobile IPv6 specific ndisc options */ -+#define ND_OPT_RTR_ADV_INTERVAL 7 -+#define ND_OPT_HOME_AGENT_INFO 8 -+ - #define MAX_RTR_SOLICITATION_DELAY HZ - - #define ND_REACHABLE_TIME (30*HZ) -@@ -57,7 +61,7 @@ struct nd_opt_hdr { - } __attribute__((__packed__)); - - struct ndisc_options { -- struct nd_opt_hdr *nd_opt_array[7]; -+ struct nd_opt_hdr *nd_opt_array[10]; - struct nd_opt_hdr *nd_opt_piend; - }; - -@@ -67,6 +71,8 @@ struct ndisc_options { - #define nd_opts_pi_end nd_opt_piend - #define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR] - #define nd_opts_mtu nd_opt_array[ND_OPT_MTU] -+#define nd_opts_rai nd_opt_array[ND_OPT_RTR_ADV_INTERVAL] -+#define nd_opts_hai nd_opt_array[ND_OPT_HOME_AGENT_INFO] - - extern struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, struct nd_opt_hdr *end); - extern struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, struct ndisc_options *ndopts); -@@ -83,6 +89,15 @@ extern void ndisc_send_ns(struct net_d - struct in6_addr *daddr, - struct in6_addr *saddr); - -+extern void ndisc_send_na(struct net_device *dev, -+ struct neighbour *neigh, -+ struct in6_addr *daddr, -+ struct in6_addr *solicited_addr, -+ int router, -+ int solicited, -+ int override, -+ int inc_opt); -+ - extern void ndisc_send_rs(struct net_device *dev, - struct in6_addr *saddr, - struct in6_addr *daddr); -diff -uprN linux-2.4.25.old/include/net/sock.h linux-2.4.25/include/net/sock.h ---- linux-2.4.25.old/include/net/sock.h 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/include/net/sock.h 2004-06-26 11:29:30.000000000 +0100 -@@ -149,7 +149,9 @@ struct ipv6_pinfo { - struct in6_addr rcv_saddr; - struct in6_addr daddr; - struct in6_addr *daddr_cache; -- -+#if defined(CONFIG_IPV6_SUBTREES) -+ struct in6_addr *saddr_cache; -+#endif - __u32 flow_label; - __u32 frag_size; - int hop_limit; -diff -uprN linux-2.4.25.old/net/Makefile linux-2.4.25/net/Makefile ---- linux-2.4.25.old/net/Makefile 2004-06-26 11:22:00.000000000 +0100 -+++ linux-2.4.25/net/Makefile 2004-06-26 11:29:30.000000000 +0100 -@@ -7,7 +7,7 @@ - - O_TARGET := network.o - --mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp -+mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp ipv6 - export-objs := netsyms.o - - subdir-y := core ethernet -@@ -25,6 +25,7 @@ subdir-$(CONFIG_IP_SCTP) += sctp - ifneq ($(CONFIG_IPV6),n) - ifneq ($(CONFIG_IPV6),) - subdir-$(CONFIG_NETFILTER) += ipv6/netfilter -+subdir-$(CONFIG_IPV6_MOBILITY) += ipv6/mobile_ip6 - endif - endif - -diff -uprN linux-2.4.25.old/net/core/neighbour.c linux-2.4.25/net/core/neighbour.c ---- linux-2.4.25.old/net/core/neighbour.c 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/net/core/neighbour.c 2004-06-26 11:29:30.000000000 +0100 -@@ -386,7 +386,7 @@ struct pneigh_entry * pneigh_lookup(stru - if (!creat) - return NULL; - -- n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL); -+ n = kmalloc(sizeof(*n) + key_len, GFP_ATOMIC); - if (n == NULL) - return NULL; - -diff -uprN linux-2.4.25.old/net/ipv6/Config.in linux-2.4.25/net/ipv6/Config.in ---- linux-2.4.25.old/net/ipv6/Config.in 2001-12-21 17:42:05.000000000 +0000 -+++ linux-2.4.25/net/ipv6/Config.in 2004-06-26 11:29:30.000000000 +0100 -@@ -1,10 +1,16 @@ - # - # IPv6 configuration - # -- -+bool ' IPv6: routing by source address (EXPERIMENTAL)' CONFIG_IPV6_SUBTREES - #bool ' IPv6: flow policy support' CONFIG_RT6_POLICY - #bool ' IPv6: firewall support' CONFIG_IPV6_FIREWALL - -+if [ "$CONFIG_IPV6" != "n" ]; then -+ dep_tristate ' IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL)' CONFIG_IPV6_TUNNEL $CONFIG_IPV6 -+fi -+ -+source net/ipv6/mobile_ip6/Config.in -+ - if [ "$CONFIG_NETFILTER" != "n" ]; then - source net/ipv6/netfilter/Config.in - fi -diff -uprN linux-2.4.25.old/net/ipv6/Makefile linux-2.4.25/net/ipv6/Makefile ---- linux-2.4.25.old/net/ipv6/Makefile 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/Makefile 2004-06-26 11:29:30.000000000 +0100 -@@ -6,18 +6,28 @@ - # unless it's something special (ie not a .c file). - # - -+export-objs := ipv6_syms.o ipv6_tunnel.o - --O_TARGET := ipv6.o -+#list-multi := ipv6.o ipv6_tunnel.o - --obj-y := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ -- route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \ -- protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ -- exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ -- ip6_flowlabel.o ipv6_syms.o -+ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ -+ sit.o route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o \ -+ raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ -+ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ -+ ip6_flowlabel.o ipv6_syms.o -+ -+ifneq ($(CONFIG_IPV6_MOBILITY),n) -+ifneq ($(CONFIG_IPV6_MOBILITY),) -+ipv6-objs += mipglue.o -+endif -+endif - --export-objs := ipv6_syms.o -+obj-$(CONFIG_IPV6) += ipv6.o -+obj-$(CONFIG_IPV6_TUNNEL) += ipv6_tunnel.o -+ -+ipv6.o: $(ipv6-objs) -+ $(LD) -r -o $@ $(ipv6-objs) - --obj-m := $(O_TARGET) - - #obj-$(CONFIG_IPV6_FIREWALL) += ip6_fw.o - -diff -uprN linux-2.4.25.old/net/ipv6/addrconf.c linux-2.4.25/net/ipv6/addrconf.c ---- linux-2.4.25.old/net/ipv6/addrconf.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/addrconf.c 2004-06-26 11:29:30.000000000 +0100 -@@ -68,6 +68,8 @@ - - #include <asm/uaccess.h> - -+#include <net/mipglue.h> -+ - #define IPV6_MAX_ADDRESSES 16 - - /* Set to 3 to get tracing... */ -@@ -103,9 +105,9 @@ static spinlock_t addrconf_verify_lock = - - static int addrconf_ifdown(struct net_device *dev, int how); - --static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); -+void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); - static void addrconf_dad_timer(unsigned long data); --static void addrconf_dad_completed(struct inet6_ifaddr *ifp); -+void addrconf_dad_completed(struct inet6_ifaddr *ifp); - static void addrconf_rs_timer(unsigned long data); - static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); - -@@ -330,38 +332,6 @@ static struct inet6_dev * ipv6_find_idev - return idev; - } - --void ipv6_addr_prefix(struct in6_addr *prefix, -- struct in6_addr *addr, int prefix_len) --{ -- unsigned long mask; -- int ncopy, nbits; -- -- memset(prefix, 0, sizeof(*prefix)); -- -- if (prefix_len <= 0) -- return; -- if (prefix_len > 128) -- prefix_len = 128; -- -- ncopy = prefix_len / 32; -- switch (ncopy) { -- case 4: prefix->s6_addr32[3] = addr->s6_addr32[3]; -- case 3: prefix->s6_addr32[2] = addr->s6_addr32[2]; -- case 2: prefix->s6_addr32[1] = addr->s6_addr32[1]; -- case 1: prefix->s6_addr32[0] = addr->s6_addr32[0]; -- case 0: break; -- } -- nbits = prefix_len % 32; -- if (nbits == 0) -- return; -- -- mask = ~((1 << (32 - nbits)) - 1); -- mask = htonl(mask); -- -- prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask; --} -- -- - static void dev_forward_change(struct inet6_dev *idev) - { - struct net_device *dev; -@@ -513,7 +483,7 @@ ipv6_add_addr(struct inet6_dev *idev, co - - /* This function wants to get referenced ifp and releases it before return */ - --static void ipv6_del_addr(struct inet6_ifaddr *ifp) -+void ipv6_del_addr(struct inet6_ifaddr *ifp) - { - struct inet6_ifaddr *ifa, **ifap; - struct inet6_dev *idev = ifp->idev; -@@ -662,6 +632,12 @@ out: - if (match) - in6_ifa_put(match); - -+ /* The home address is always used as source address in -+ * MIPL mobile IPv6 -+ */ -+ if (scope != IFA_HOST && scope != IFA_LINK) -+ addrconf_get_mipv6_home_address(saddr); -+ - return err; - } - -@@ -815,7 +791,7 @@ void addrconf_leave_solict(struct net_de - } - - --static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) -+int ipv6_generate_eui64(u8 *eui, struct net_device *dev) - { - switch (dev->type) { - case ARPHRD_ETHER: -@@ -840,7 +816,7 @@ static int ipv6_generate_eui64(u8 *eui, - return -1; - } - --static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) -+int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) - { - int err = -1; - struct inet6_ifaddr *ifp; -@@ -1407,6 +1383,24 @@ static void addrconf_sit_config(struct n - sit_route_add(dev); - } - -+/** -+ * addrconf_ipv6_tunnel_config - configure IPv6 tunnel device -+ * @dev: tunnel device -+ **/ -+ -+static void addrconf_ipv6_tunnel_config(struct net_device *dev) -+{ -+ struct inet6_dev *idev; -+ -+ ASSERT_RTNL(); -+ -+ /* Assign inet6_dev structure to tunnel device */ -+ if ((idev = ipv6_find_idev(dev)) == NULL) { -+ printk(KERN_DEBUG "init ipv6 tunnel: add_dev failed\n"); -+ return; -+ } -+} -+ - - int addrconf_notify(struct notifier_block *this, unsigned long event, - void * data) -@@ -1421,6 +1415,10 @@ int addrconf_notify(struct notifier_bloc - addrconf_sit_config(dev); - break; - -+ case ARPHRD_TUNNEL6: -+ addrconf_ipv6_tunnel_config(dev); -+ break; -+ - case ARPHRD_LOOPBACK: - init_loopback(dev); - break; -@@ -1602,7 +1600,7 @@ out: - /* - * Duplicate Address Detection - */ --static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) -+void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) - { - struct net_device *dev; - unsigned long rand_num; -@@ -1667,7 +1665,7 @@ static void addrconf_dad_timer(unsigned - in6_ifa_put(ifp); - } - --static void addrconf_dad_completed(struct inet6_ifaddr *ifp) -+void addrconf_dad_completed(struct inet6_ifaddr *ifp) - { - struct net_device * dev = ifp->idev->dev; - -@@ -1676,7 +1674,7 @@ static void addrconf_dad_completed(struc - */ - - ipv6_ifa_notify(RTM_NEWADDR, ifp); -- -+ notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifp); - /* If added prefix is link local and forwarding is off, - start sending router solicitations. - */ -@@ -1877,8 +1875,20 @@ inet6_rtm_newaddr(struct sk_buff *skb, s - if (rta[IFA_LOCAL-1]) { - if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) - return -EINVAL; -+ if (ifm->ifa_flags & IFA_F_HOMEADDR && !rta[IFA_HOMEAGENT-1]) -+ return -EINVAL; - pfx = RTA_DATA(rta[IFA_LOCAL-1]); - } -+ if (rta[IFA_HOMEAGENT-1]) { -+ struct in6_addr *ha; -+ if (pfx == NULL || !(ifm->ifa_flags & IFA_F_HOMEADDR)) -+ return -EINVAL; -+ if (RTA_PAYLOAD(rta[IFA_HOMEAGENT-1]) < sizeof(*ha)) -+ return -EINVAL; -+ ha = RTA_DATA(rta[IFA_HOMEAGENT-1]); -+ addrconf_set_mipv6_mn_home(ifm->ifa_index, pfx, ifm->ifa_prefixlen, -+ ha, ifm->ifa_prefixlen); -+ } - if (pfx == NULL) - return -EINVAL; - -diff -uprN linux-2.4.25.old/net/ipv6/af_inet6.c linux-2.4.25/net/ipv6/af_inet6.c ---- linux-2.4.25.old/net/ipv6/af_inet6.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/af_inet6.c 2004-06-26 11:29:30.000000000 +0100 -@@ -58,6 +58,9 @@ - #include <net/transp_v6.h> - #include <net/ip6_route.h> - #include <net/addrconf.h> -+#ifdef CONFIG_IPV6_TUNNEL -+#include <net/ipv6_tunnel.h> -+#endif - - #include <asm/uaccess.h> - #include <asm/system.h> -@@ -646,6 +649,11 @@ static int __init inet6_init(void) - err = ndisc_init(&inet6_family_ops); - if (err) - goto ndisc_fail; -+#ifdef CONFIG_IPV6_TUNNEL -+ err = ip6_tunnel_init(); -+ if (err) -+ goto ip6_tunnel_fail; -+#endif - err = igmp6_init(&inet6_family_ops); - if (err) - goto igmp_fail; -@@ -698,6 +706,10 @@ proc_raw6_fail: - #endif - igmp_fail: - ndisc_cleanup(); -+#ifdef CONFIG_IPV6_TUNNEL -+ ip6_tunnel_cleanup(); -+ip6_tunnel_fail: -+#endif - ndisc_fail: - icmpv6_cleanup(); - icmp_fail: -@@ -730,6 +742,9 @@ static void inet6_exit(void) - ip6_route_cleanup(); - ipv6_packet_cleanup(); - igmp6_cleanup(); -+#ifdef CONFIG_IPV6_TUNNEL -+ ip6_tunnel_cleanup(); -+#endif - ndisc_cleanup(); - icmpv6_cleanup(); - #ifdef CONFIG_SYSCTL -diff -uprN linux-2.4.25.old/net/ipv6/exthdrs.c linux-2.4.25/net/ipv6/exthdrs.c ---- linux-2.4.25.old/net/ipv6/exthdrs.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/exthdrs.c 2004-06-26 11:29:30.000000000 +0100 -@@ -41,6 +41,9 @@ - #include <net/ip6_route.h> - #include <net/addrconf.h> - -+#include <net/mipglue.h> -+#include <net/mipv6.h> -+ - #include <asm/uaccess.h> - - /* -@@ -160,7 +163,8 @@ bad: - *****************************/ - - struct tlvtype_proc tlvprocdestopt_lst[] = { -- /* No destination options are defined now */ -+ /* Mobility Support destination options */ -+ {MIPV6_TLV_HOMEADDR, mipv6_handle_dstopt}, - {-1, NULL} - }; - -@@ -210,6 +214,7 @@ static int ipv6_routing_header(struct sk - - struct ipv6_rt_hdr *hdr; - struct rt0_hdr *rthdr; -+ struct rt2_hdr *rt2hdr; - - if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || - !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { -@@ -225,17 +230,25 @@ static int ipv6_routing_header(struct sk - kfree_skb(skb); - return -1; - } -- -+ /* Silently discard invalid packets containing RTH type 2 */ -+ if (hdr->type == IPV6_SRCRT_TYPE_2 && -+ (hdr->hdrlen != 2 || hdr->segments_left != 1)) { -+ kfree_skb(skb); -+ return -1; -+ } - looped_back: - if (hdr->segments_left == 0) { -- opt->srcrt = skb->h.raw - skb->nh.raw; -+ if (hdr->type == IPV6_SRCRT_TYPE_0) -+ opt->srcrt = skb->h.raw - skb->nh.raw; -+ else if (hdr->type == IPV6_SRCRT_TYPE_2) -+ opt->srcrt2 = skb->h.raw - skb->nh.raw; - skb->h.raw += (hdr->hdrlen + 1) << 3; - opt->dst0 = opt->dst1; - opt->dst1 = 0; - return (&hdr->nexthdr) - skb->nh.raw; - } - -- if (hdr->type != IPV6_SRCRT_TYPE_0) { -+ if (hdr->type != IPV6_SRCRT_TYPE_0 && hdr->type != IPV6_SRCRT_TYPE_2) { - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); - return -1; - } -@@ -275,9 +288,20 @@ looped_back: - - i = n - --hdr->segments_left; - -- rthdr = (struct rt0_hdr *) hdr; -- addr = rthdr->addr; -- addr += i - 1; -+ if (hdr->type == IPV6_SRCRT_TYPE_0) { -+ rthdr = (struct rt0_hdr *) hdr; -+ addr = rthdr->addr; -+ addr += i - 1; -+ } else { -+ /* check that address is this node's home address */ -+ rt2hdr = (struct rt2_hdr *) hdr; -+ addr = &rt2hdr->addr; -+ if (!ipv6_chk_addr(addr, NULL) || -+ !ipv6_chk_mip_home_addr(addr)) { -+ kfree_skb(skb); -+ return -1; -+ } -+ } - - addr_type = ipv6_addr_type(addr); - -@@ -330,6 +354,10 @@ looped_back: - temporary (or permanent) backdoor. - If listening socket set IPV6_RTHDR to 2, then we invert header. - --ANK (980729) -+ -+ By the Mobile IPv6 specification Type 2 routing header MUST NOT be -+ inverted. -+ --AJT (20020917) - */ - - struct ipv6_txoptions * -@@ -352,6 +380,18 @@ ipv6_invert_rthdr(struct sock *sk, struc - struct ipv6_txoptions *opt; - int hdrlen = ipv6_optlen(hdr); - -+ if (hdr->type == IPV6_SRCRT_TYPE_2) { -+ opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC); -+ if (opt == NULL) -+ return NULL; -+ memset(opt, 0, sizeof(*opt)); -+ opt->tot_len = sizeof(*opt) + hdrlen; -+ opt->srcrt = (void*)(opt+1); -+ opt->opt_nflen = hdrlen; -+ memcpy(opt->srcrt, hdr, sizeof(struct rt2_hdr)); -+ return opt; -+ } -+ - if (hdr->segments_left || - hdr->type != IPV6_SRCRT_TYPE_0 || - hdr->hdrlen & 0x01) -@@ -622,8 +662,18 @@ u8 *ipv6_build_nfrag_opts(struct sk_buff - if (opt) { - if (opt->dst0opt) - prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt); -- if (opt->srcrt) -- prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); -+ if (opt->srcrt) { -+ if (opt->srcrt2) { -+ struct in6_addr *rt2_hop = &((struct rt2_hdr *)opt->srcrt2)->addr; -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, rt2_hop); -+ } else -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); -+ } -+ if (opt->srcrt2) { -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; -+ ipv6_addr_copy(&parm->hoa, daddr); -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt2, daddr); -+ } - } - return prev_hdr; - } -@@ -684,6 +734,11 @@ void ipv6_push_nfrag_opts(struct sk_buff - u8 *proto, - struct in6_addr **daddr) - { -+ if (opt->srcrt2) { -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; -+ ipv6_addr_copy(&parm->hoa, *daddr); -+ ipv6_push_rthdr(skb, proto, opt->srcrt2, daddr); -+ } - if (opt->srcrt) - ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); - if (opt->dst0opt) -@@ -719,6 +774,8 @@ ipv6_dup_options(struct sock *sk, struct - *((char**)&opt2->auth) += dif; - if (opt2->srcrt) - *((char**)&opt2->srcrt) += dif; -+ if (opt2->srcrt2) -+ *((char**)&opt2->srcrt2) += dif; - } - return opt2; - } -diff -uprN linux-2.4.25.old/net/ipv6/icmp.c linux-2.4.25/net/ipv6/icmp.c ---- linux-2.4.25.old/net/ipv6/icmp.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/icmp.c 2004-06-26 11:29:30.000000000 +0100 -@@ -61,6 +61,8 @@ - #include <net/addrconf.h> - #include <net/icmp.h> - -+#include <net/mipglue.h> -+ - #include <asm/uaccess.h> - #include <asm/system.h> - -@@ -364,6 +366,8 @@ void icmpv6_send(struct sk_buff *skb, in - - msg.len = len; - -+ icmpv6_swap_mipv6_addrs(skb); -+ - ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, - MSG_DONTWAIT); - if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) -@@ -562,13 +566,13 @@ int icmpv6_rcv(struct sk_buff *skb) - rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev, - ntohl(hdr->icmp6_mtu)); - -- /* -- * Drop through to notify -- */ -+ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); -+ break; - - case ICMPV6_DEST_UNREACH: -- case ICMPV6_TIME_EXCEED: - case ICMPV6_PARAMPROB: -+ mipv6_icmp_rcv(skb); -+ case ICMPV6_TIME_EXCEED: - icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); - break; - -@@ -597,6 +601,13 @@ int icmpv6_rcv(struct sk_buff *skb) - case ICMPV6_MGM_REDUCTION: - break; - -+ case MIPV6_DHAAD_REQUEST: -+ case MIPV6_DHAAD_REPLY: -+ case MIPV6_PREFIX_SOLICIT: -+ case MIPV6_PREFIX_ADV: -+ mipv6_icmp_rcv(skb); -+ break; -+ - default: - if (net_ratelimit()) - printk(KERN_DEBUG "icmpv6: msg of unkown type\n"); -diff -uprN linux-2.4.25.old/net/ipv6/ip6_fib.c linux-2.4.25/net/ipv6/ip6_fib.c ---- linux-2.4.25.old/net/ipv6/ip6_fib.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ip6_fib.c 2004-06-26 11:29:30.000000000 +0100 -@@ -18,6 +18,7 @@ - * Yuji SEKIYA @USAGI: Support default route on router node; - * remove ip6_null_entry from the top of - * routing table. -+ * Ville Nuorvala: Fixes to source address based routing - */ - #include <linux/config.h> - #include <linux/errno.h> -@@ -40,7 +41,6 @@ - #include <net/ip6_route.h> - - #define RT6_DEBUG 2 --#undef CONFIG_IPV6_SUBTREES - - #if RT6_DEBUG >= 3 - #define RT6_TRACE(x...) printk(KERN_DEBUG x) -@@ -500,6 +500,8 @@ static __inline__ void fib6_start_gc(str - mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); - } - -+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn); -+ - /* - * Add routing information to the routing tree. - * <destination addr>/<source addr> -@@ -508,17 +510,19 @@ static __inline__ void fib6_start_gc(str - - int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh) - { -- struct fib6_node *fn; -+ struct fib6_node *fn = root; - int err = -ENOMEM; - -- fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), -- rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); -+#ifdef CONFIG_IPV6_SUBTREES -+ struct fib6_node *pn = NULL; - -+ fn = fib6_add_1(root, &rt->rt6i_src.addr, sizeof(struct in6_addr), -+ rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); -+ - if (fn == NULL) - goto out; - --#ifdef CONFIG_IPV6_SUBTREES -- if (rt->rt6i_src.plen) { -+ if (rt->rt6i_dst.plen) { - struct fib6_node *sn; - - if (fn->subtree == NULL) { -@@ -546,9 +550,9 @@ int fib6_add(struct fib6_node *root, str - - /* Now add the first leaf node to new subtree */ - -- sn = fib6_add_1(sfn, &rt->rt6i_src.addr, -- sizeof(struct in6_addr), rt->rt6i_src.plen, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ sn = fib6_add_1(sfn, &rt->rt6i_dst.addr, -+ sizeof(struct in6_addr), rt->rt6i_dst.plen, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - - if (sn == NULL) { - /* If it is failed, discard just allocated -@@ -562,21 +566,30 @@ int fib6_add(struct fib6_node *root, str - /* Now link new subtree to main tree */ - sfn->parent = fn; - fn->subtree = sfn; -- if (fn->leaf == NULL) { -- fn->leaf = rt; -- atomic_inc(&rt->rt6i_ref); -- } - } else { -- sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, -- sizeof(struct in6_addr), rt->rt6i_src.plen, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ sn = fib6_add_1(fn->subtree, &rt->rt6i_dst.addr, -+ sizeof(struct in6_addr), rt->rt6i_dst.plen, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - - if (sn == NULL) - goto st_failure; - } - -+ /* fib6_add_1 might have cleared the old leaf pointer */ -+ if (fn->leaf == NULL) { -+ fn->leaf = rt; -+ atomic_inc(&rt->rt6i_ref); -+ } -+ -+ pn = fn; - fn = sn; - } -+#else -+ fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), -+ rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); -+ -+ if (fn == NULL) -+ goto out; - #endif - - err = fib6_add_rt2node(fn, rt, nlh); -@@ -588,8 +601,30 @@ int fib6_add(struct fib6_node *root, str - } - - out: -- if (err) -+ if (err) { -+#ifdef CONFIG_IPV6_SUBTREES -+ -+ /* If fib6_add_1 has cleared the old leaf pointer in the -+ super-tree leaf node, we have to find a new one for it. -+ -+ This situation will never arise in the sub-tree since -+ the node will at least have the route that caused -+ fib6_add_rt2node to fail. -+ */ -+ -+ if (pn && !(pn->fn_flags & RTN_RTINFO)) { -+ pn->leaf = fib6_find_prefix(pn); -+#if RT6_DEBUG >= 2 -+ if (!pn->leaf) { -+ BUG_TRAP(pn->leaf); -+ pn->leaf = &ip6_null_entry; -+ } -+#endif -+ atomic_inc(&pn->leaf->rt6i_ref); -+ } -+#endif - dst_free(&rt->u.dst); -+ } - return err; - - #ifdef CONFIG_IPV6_SUBTREES -@@ -597,8 +632,8 @@ out: - is orphan. If it is, shoot it. - */ - st_failure: -- if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT)) -- fib_repair_tree(fn); -+ if (fn && !(fn->fn_flags & (RTN_RTINFO | RTN_ROOT))) -+ fib6_repair_tree(fn); - dst_free(&rt->u.dst); - return err; - #endif -@@ -641,22 +676,28 @@ static struct fib6_node * fib6_lookup_1( - break; - } - -- while ((fn->fn_flags & RTN_ROOT) == 0) { -+ for (;;) { - #ifdef CONFIG_IPV6_SUBTREES - if (fn->subtree) { -- struct fib6_node *st; -- struct lookup_args *narg; -- -- narg = args + 1; -- -- if (narg->addr) { -- st = fib6_lookup_1(fn->subtree, narg); -+ struct rt6key *key; - -- if (st && !(st->fn_flags & RTN_ROOT)) -- return st; -+ key = (struct rt6key *) ((u8 *) fn->leaf + -+ args->offset); -+ -+ if (addr_match(&key->addr, args->addr, key->plen)) { -+ struct fib6_node *st; -+ struct lookup_args *narg = args + 1; -+ if (!ipv6_addr_any(narg->addr)) { -+ st = fib6_lookup_1(fn->subtree, narg); -+ -+ if (st && !(st->fn_flags & RTN_ROOT)) -+ return st; -+ } - } - } - #endif -+ if (fn->fn_flags & RTN_ROOT) -+ break; - - if (fn->fn_flags & RTN_RTINFO) { - struct rt6key *key; -@@ -680,13 +721,22 @@ struct fib6_node * fib6_lookup(struct fi - struct lookup_args args[2]; - struct rt6_info *rt = NULL; - struct fib6_node *fn; -+#ifdef CONFIG_IPV6_SUBTREES -+ struct in6_addr saddr_buf; -+#endif - -+#ifdef CONFIG_IPV6_SUBTREES -+ if (saddr == NULL) { -+ memset(&saddr_buf, 0, sizeof(struct in6_addr)); -+ saddr = &saddr_buf; -+ } -+ args[0].offset = (u8*) &rt->rt6i_src - (u8*) rt; -+ args[0].addr = saddr; -+ args[1].offset = (u8*) &rt->rt6i_dst - (u8*) rt; -+ args[1].addr = daddr; -+#else - args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt; - args[0].addr = daddr; -- --#ifdef CONFIG_IPV6_SUBTREES -- args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt; -- args[1].addr = saddr; - #endif - - fn = fib6_lookup_1(root, args); -@@ -739,19 +789,25 @@ struct fib6_node * fib6_locate(struct fi - { - struct rt6_info *rt = NULL; - struct fib6_node *fn; -- -- fn = fib6_locate_1(root, daddr, dst_len, -- (u8*) &rt->rt6i_dst - (u8*) rt); -- - #ifdef CONFIG_IPV6_SUBTREES -- if (src_len) { -- BUG_TRAP(saddr!=NULL); -- if (fn == NULL) -- fn = fn->subtree; -+ struct in6_addr saddr_buf; -+ -+ if (saddr == NULL) { -+ memset(&saddr_buf, 0, sizeof(struct in6_addr)); -+ saddr = &saddr_buf; -+ } -+ fn = fib6_locate_1(root, saddr, src_len, -+ (u8*) &rt->rt6i_src - (u8*) rt); -+ if (dst_len) { - if (fn) -- fn = fib6_locate_1(fn, saddr, src_len, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ fn = fib6_locate_1(fn->subtree, daddr, dst_len, -+ (u8*) &rt->rt6i_dst - (u8*) rt); -+ else -+ return NULL; - } -+#else -+ fn = fib6_locate_1(root, daddr, dst_len, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - #endif - - if (fn && fn->fn_flags&RTN_RTINFO) -@@ -939,7 +995,7 @@ static void fib6_del_route(struct fib6_n - } - fn = fn->parent; - } -- /* No more references are possiible at this point. */ -+ /* No more references are possible at this point. */ - if (atomic_read(&rt->rt6i_ref) != 1) BUG(); - } - -diff -uprN linux-2.4.25.old/net/ipv6/ip6_input.c linux-2.4.25/net/ipv6/ip6_input.c ---- linux-2.4.25.old/net/ipv6/ip6_input.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ip6_input.c 2004-06-26 11:29:30.000000000 +0100 -@@ -40,13 +40,42 @@ - #include <net/ip6_route.h> - #include <net/addrconf.h> - -+static inline int ip6_proxy_chk(struct sk_buff *skb) -+{ -+ struct ipv6hdr *hdr = skb->nh.ipv6h; - -- -+ if (ipv6_addr_type(&hdr->daddr)&IPV6_ADDR_UNICAST && -+ pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { -+ u8 nexthdr = hdr->nexthdr; -+ int offset; -+ struct icmp6hdr msg; -+ -+ if (ipv6_ext_hdr(nexthdr)) { -+ offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, -+ skb->len - sizeof(*hdr)); -+ if (offset < 0) -+ return 0; -+ } else -+ offset = sizeof(*hdr); -+ -+ /* capture unicast NUD probes on behalf of the proxied node */ -+ -+ if (nexthdr == IPPROTO_ICMPV6 && -+ !skb_copy_bits(skb, offset, &msg, sizeof(msg)) && -+ msg.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) { -+ return 1; -+ } -+ } -+ return 0; -+} -+ - static inline int ip6_rcv_finish( struct sk_buff *skb) - { -- if (skb->dst == NULL) -- ip6_route_input(skb); -- -+ if (skb->dst == NULL) { -+ if (ip6_proxy_chk(skb)) -+ return ip6_input(skb); -+ ip6_route_input(skb); -+ } - return skb->dst->input(skb); - } - -diff -uprN linux-2.4.25.old/net/ipv6/ip6_output.c linux-2.4.25/net/ipv6/ip6_output.c ---- linux-2.4.25.old/net/ipv6/ip6_output.c 2003-08-25 12:44:44.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ip6_output.c 2004-06-26 11:29:30.000000000 +0100 -@@ -50,6 +50,8 @@ - #include <net/rawv6.h> - #include <net/icmp.h> - -+#include <net/mipglue.h> -+ - static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr) - { - static u32 ipv6_fragmentation_id = 1; -@@ -194,7 +196,14 @@ int ip6_xmit(struct sock *sk, struct sk_ - u8 proto = fl->proto; - int seg_len = skb->len; - int hlimit; -+ int retval; -+ struct ipv6_txoptions *orig_opt = opt; -+ -+ opt = ip6_add_mipv6_txoptions(sk, skb, orig_opt, fl, &dst); - -+ if(orig_opt && !opt) -+ return -ENOMEM; -+ - if (opt) { - int head_room; - -@@ -209,8 +218,11 @@ int ip6_xmit(struct sock *sk, struct sk_ - struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room); - kfree_skb(skb); - skb = skb2; -- if (skb == NULL) -+ if (skb == NULL) { -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return -ENOBUFS; -+ } - if (sk) - skb_set_owner_w(skb, sk); - } -@@ -242,7 +254,10 @@ int ip6_xmit(struct sock *sk, struct sk_ - - if (skb->len <= dst->pmtu) { - IP6_INC_STATS(Ip6OutRequests); -- return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); -+ ip6_mark_mipv6_packet(opt, skb); -+ retval = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ return retval; - } - - if (net_ratelimit()) -@@ -250,6 +265,9 @@ int ip6_xmit(struct sock *sk, struct sk_ - skb->dev = dst->dev; - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev); - kfree_skb(skb); -+ -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return -EMSGSIZE; - } - -@@ -473,6 +491,7 @@ static int ip6_frag_xmit(struct sock *sk - - IP6_INC_STATS(Ip6FragCreates); - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, skb); - err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); - if (err) { - kfree_skb(last_skb); -@@ -499,6 +518,7 @@ static int ip6_frag_xmit(struct sock *sk - IP6_INC_STATS(Ip6FragCreates); - IP6_INC_STATS(Ip6FragOKs); - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, last_skb); - return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute); - } - -@@ -509,26 +529,43 @@ int ip6_build_xmit(struct sock *sk, inet - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - struct in6_addr *final_dst = NULL; - struct dst_entry *dst; -+ struct rt6_info *rt; - int err = 0; - unsigned int pktlength, jumbolen, mtu; - struct in6_addr saddr; -+ struct ipv6_txoptions *orig_opt = opt; -+#ifdef CONFIG_IPV6_SUBTREES -+ struct dst_entry *org_dst; -+#endif -+ -+ opt = ip6_add_mipv6_txoptions(sk, NULL, orig_opt, fl, NULL); -+ -+ if(orig_opt && !opt) -+ return -ENOMEM; - - if (opt && opt->srcrt) { - struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; - final_dst = fl->fl6_dst; - fl->fl6_dst = rt0->addr; -- } -+ } else if (opt && opt->srcrt2) { -+ struct rt2_hdr *rt2 = (struct rt2_hdr *) opt->srcrt2; -+ final_dst = fl->fl6_dst; -+ fl->fl6_dst = &rt2->addr; -+ } - - if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr)) - fl->oif = np->mcast_oif; - - dst = __sk_dst_check(sk, np->dst_cookie); -+#ifdef CONFIG_IPV6_SUBTREES -+ org_dst = dst; -+#endif - if (dst) { -- struct rt6_info *rt = (struct rt6_info*)dst; -+ rt = (struct rt6_info*)dst; - - /* Yes, checking route validity in not connected - case is not very simple. Take into account, -- that we do not support routing by source, TOS, -+ that we do not support routing by TOS, - and MSG_DONTROUTE --ANK (980726) - - 1. If route was host route, check that -@@ -548,6 +585,13 @@ int ip6_build_xmit(struct sock *sk, inet - ipv6_addr_cmp(fl->fl6_dst, &rt->rt6i_dst.addr)) - && (np->daddr_cache == NULL || - ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache))) -+#ifdef CONFIG_IPV6_SUBTREES -+ || (fl->fl6_src != NULL -+ && (rt->rt6i_src.plen != 128 || -+ ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) -+ && (np->saddr_cache == NULL || -+ ipv6_addr_cmp(fl->fl6_src, np->saddr_cache))) -+#endif - || (fl->oif && fl->oif != dst->dev->ifindex)) { - dst = NULL; - } else -@@ -560,21 +604,42 @@ int ip6_build_xmit(struct sock *sk, inet - if (dst->error) { - IP6_INC_STATS(Ip6OutNoRoutes); - dst_release(dst); -+ ip6_free_mipv6_txoptions(opt, orig_opt); - return -ENETUNREACH; - } - - if (fl->fl6_src == NULL) { - err = ipv6_get_saddr(dst, fl->fl6_dst, &saddr); -- - if (err) { - #if IP6_DEBUG >= 2 - printk(KERN_DEBUG "ip6_build_xmit: " - "no available source address\n"); - #endif -+ -+#ifdef CONFIG_IPV6_SUBTREES -+ if (dst != org_dst) { -+ dst_release(dst); -+ dst = org_dst; -+ } -+#endif - goto out; - } - fl->fl6_src = &saddr; - } -+#ifdef CONFIG_IPV6_SUBTREES -+ rt = (struct rt6_info*)dst; -+ if (dst != org_dst || rt->rt6i_src.plen != 128 || -+ ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) { -+ dst_release(dst); -+ dst = ip6_route_output(sk, fl); -+ if (dst->error) { -+ IP6_INC_STATS(Ip6OutNoRoutes); -+ dst_release(dst); -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ return -ENETUNREACH; -+ } -+ } -+#endif - pktlength = length; - - if (hlimit < 0) { -@@ -667,6 +732,7 @@ int ip6_build_xmit(struct sock *sk, inet - - if (!err) { - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, skb); - err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); - } else { - err = -EFAULT; -@@ -688,9 +754,14 @@ int ip6_build_xmit(struct sock *sk, inet - * cleanup - */ - out: -- ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL); -+ ip6_dst_store(sk, dst, -+ fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL, -+ fl->nl_u.ip6_u.saddr == &np->saddr ? &np->saddr : NULL); - if (err > 0) - err = np->recverr ? net_xmit_errno(err) : 0; -+ -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return err; - } - -@@ -769,6 +840,15 @@ int ip6_forward(struct sk_buff *skb) - return -ETIMEDOUT; - } - -+ /* The proxying router can't forward traffic sent to a link-local -+ address, so signal the sender and discard the packet. This -+ behavior is required by the MIPv6 specification. */ -+ -+ if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL && -+ skb->dev && pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { -+ dst_link_failure(skb); -+ goto drop; -+ } - /* IPv6 specs say nothing about it, but it is clear that we cannot - send redirects to source routed frames. - */ -diff -uprN linux-2.4.25.old/net/ipv6/ipv6_syms.c linux-2.4.25/net/ipv6/ipv6_syms.c ---- linux-2.4.25.old/net/ipv6/ipv6_syms.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/ipv6_syms.c 2004-06-26 11:29:30.000000000 +0100 -@@ -6,6 +6,8 @@ - #include <net/ipv6.h> - #include <net/addrconf.h> - #include <net/ip6_route.h> -+#include <net/ndisc.h> -+#include <net/mipglue.h> - - EXPORT_SYMBOL(ipv6_addr_type); - EXPORT_SYMBOL(icmpv6_send); -@@ -33,3 +35,48 @@ EXPORT_SYMBOL(inet6_ioctl); - EXPORT_SYMBOL(ipv6_get_saddr); - EXPORT_SYMBOL(ipv6_chk_addr); - EXPORT_SYMBOL(in6_dev_finish_destroy); -+ -+#if defined(CONFIG_IPV6_TUNNEL_MODULE) || defined(CONFIG_IPV6_MOBILITY_MODULE) -+EXPORT_SYMBOL(ip6_build_xmit); -+EXPORT_SYMBOL(rt6_lookup); -+EXPORT_SYMBOL(ipv6_ext_hdr); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MODULE -+EXPORT_SYMBOL(mipv6_functions); -+EXPORT_SYMBOL(mipv6_invalidate_calls); -+#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE) -+EXPORT_SYMBOL(ip6_route_add); -+EXPORT_SYMBOL(ip6_route_del); -+EXPORT_SYMBOL(ipv6_get_lladdr); -+EXPORT_SYMBOL(ipv6_get_ifaddr); -+EXPORT_SYMBOL(nd_tbl); -+EXPORT_SYMBOL(ndisc_send_ns); -+EXPORT_SYMBOL(ndisc_send_na); -+EXPORT_SYMBOL(ndisc_next_option); -+EXPORT_SYMBOL(inet6_ifa_finish_destroy); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE -+EXPORT_SYMBOL(ipv6_dev_ac_dec); -+EXPORT_SYMBOL(ipv6_dev_ac_inc); -+EXPORT_SYMBOL(ipv6_dev_mc_dec); -+EXPORT_SYMBOL(ipv6_dev_mc_inc); -+EXPORT_SYMBOL(ip6_forward); -+EXPORT_SYMBOL(ip6_input); -+EXPORT_SYMBOL(ipv6_chk_acast_addr); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE -+#endif -+EXPORT_SYMBOL(addrconf_add_ifaddr); -+EXPORT_SYMBOL(addrconf_del_ifaddr); -+EXPORT_SYMBOL(addrconf_dad_start); -+EXPORT_SYMBOL(ip6_del_rt); -+EXPORT_SYMBOL(ip6_routing_table); -+EXPORT_SYMBOL(rt6_get_dflt_router); -+EXPORT_SYMBOL(rt6_purge_dflt_routers); -+EXPORT_SYMBOL(rt6_lock); -+EXPORT_SYMBOL(ndisc_send_rs); -+EXPORT_SYMBOL(fib6_clean_tree); -+EXPORT_SYMBOL(ipv6_del_addr); -+EXPORT_SYMBOL(ipv6_generate_eui64); -+EXPORT_SYMBOL(ipv6_inherit_eui64); -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/ipv6_tunnel.c linux-2.4.25/net/ipv6/ipv6_tunnel.c ---- linux-2.4.25.old/net/ipv6/ipv6_tunnel.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/ipv6_tunnel.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,1604 @@ -+/* -+ * IPv6 over IPv6 tunnel device -+ * Linux INET6 implementation -+ * -+ * Authors: -+ * Ville Nuorvala <vnuorval@tcs.hut.fi> -+ * -+ * $Id$ -+ * -+ * Based on: -+ * linux/net/ipv6/sit.c -+ * -+ * RFC 2473 -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/errno.h> -+#include <linux/types.h> -+#include <linux/socket.h> -+#include <linux/sockios.h> -+#include <linux/if.h> -+#include <linux/in.h> -+#include <linux/ip.h> -+#include <linux/if_tunnel.h> -+#include <linux/net.h> -+#include <linux/in6.h> -+#include <linux/netdevice.h> -+#include <linux/if_arp.h> -+#include <linux/icmpv6.h> -+#include <linux/init.h> -+#include <linux/route.h> -+#include <linux/rtnetlink.h> -+#include <linux/tqueue.h> -+ -+#include <asm/uaccess.h> -+#include <asm/atomic.h> -+ -+#include <net/sock.h> -+#include <net/ipv6.h> -+#include <net/protocol.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/ipv6_tunnel.h> -+ -+MODULE_AUTHOR("Ville Nuorvala"); -+MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel"); -+MODULE_LICENSE("GPL"); -+ -+#define IPV6_TLV_TEL_DST_SIZE 8 -+ -+#ifdef IP6_TNL_DEBUG -+#define IP6_TNL_TRACE(x...) printk(KERN_DEBUG "%s:" x "\n", __FUNCTION__) -+#else -+#define IP6_TNL_TRACE(x...) do {;} while(0) -+#endif -+ -+#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) -+ -+#define HASH_SIZE 32 -+ -+#define HASH(addr) (((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \ -+ (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ -+ (HASH_SIZE - 1)) -+ -+static int ip6ip6_fb_tnl_dev_init(struct net_device *dev); -+static int ip6ip6_tnl_dev_init(struct net_device *dev); -+ -+/* the IPv6 IPv6 tunnel fallback device */ -+static struct net_device ip6ip6_fb_tnl_dev = { -+ name: "ip6tnl0", -+ init: ip6ip6_fb_tnl_dev_init -+}; -+ -+/* the IPv6 IPv6 fallback tunnel */ -+static struct ip6_tnl ip6ip6_fb_tnl = { -+ dev: &ip6ip6_fb_tnl_dev, -+ parms:{name: "ip6tnl0", proto: IPPROTO_IPV6} -+}; -+ -+/* lists for storing tunnels in use */ -+static struct ip6_tnl *tnls_r_l[HASH_SIZE]; -+static struct ip6_tnl *tnls_wc[1]; -+static struct ip6_tnl **tnls[2] = { tnls_wc, tnls_r_l }; -+ -+/* list for unused cached kernel tunnels */ -+static struct ip6_tnl *tnls_kernel[1]; -+/* maximum number of cached kernel tunnels */ -+static unsigned int max_kdev_count = 0; -+/* minimum number of cached kernel tunnels */ -+static unsigned int min_kdev_count = 0; -+/* current number of cached kernel tunnels */ -+static unsigned int kdev_count = 0; -+ -+/* lists for tunnel hook functions */ -+static struct list_head hooks[IP6_TNL_MAXHOOKS]; -+ -+/* locks for the different lists */ -+static rwlock_t ip6ip6_lock = RW_LOCK_UNLOCKED; -+static rwlock_t ip6ip6_kernel_lock = RW_LOCK_UNLOCKED; -+static rwlock_t ip6ip6_hook_lock = RW_LOCK_UNLOCKED; -+ -+/* flag indicating if the module is being removed */ -+static int shutdown = 0; -+ -+/** -+ * ip6ip6_tnl_lookup - fetch tunnel matching the end-point addresses -+ * @remote: the address of the tunnel exit-point -+ * @local: the address of the tunnel entry-point -+ * -+ * Return: -+ * tunnel matching given end-points if found, -+ * else fallback tunnel if its device is up, -+ * else %NULL -+ **/ -+ -+struct ip6_tnl * -+ip6ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local) -+{ -+ unsigned h0 = HASH(remote); -+ unsigned h1 = HASH(local); -+ struct ip6_tnl *t; -+ -+ for (t = tnls_r_l[h0 ^ h1]; t; t = t->next) { -+ if (!ipv6_addr_cmp(local, &t->parms.laddr) && -+ !ipv6_addr_cmp(remote, &t->parms.raddr) && -+ (t->dev->flags & IFF_UP)) -+ return t; -+ } -+ if ((t = tnls_wc[0]) != NULL && (t->dev->flags & IFF_UP)) -+ return t; -+ -+ return NULL; -+} -+ -+/** -+ * ip6ip6_bucket - get head of list matching given tunnel parameters -+ * @p: parameters containing tunnel end-points -+ * -+ * Description: -+ * ip6ip6_bucket() returns the head of the list matching the -+ * &struct in6_addr entries laddr and raddr in @p. -+ * -+ * Return: head of IPv6 tunnel list -+ **/ -+ -+static struct ip6_tnl ** -+ip6ip6_bucket(struct ip6_tnl_parm *p) -+{ -+ struct in6_addr *remote = &p->raddr; -+ struct in6_addr *local = &p->laddr; -+ unsigned h = 0; -+ int prio = 0; -+ -+ if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { -+ prio = 1; -+ h = HASH(remote) ^ HASH(local); -+ } -+ return &tnls[prio][h]; -+} -+ -+/** -+ * ip6ip6_kernel_tnl_link - add new kernel tunnel to cache -+ * @t: kernel tunnel -+ * -+ * Note: -+ * %IP6_TNL_F_KERNEL_DEV is assumed to be raised in t->parms.flags. -+ * See the comments on ip6ip6_kernel_tnl_add() for more information. -+ **/ -+ -+static inline void -+ip6ip6_kernel_tnl_link(struct ip6_tnl *t) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ t->next = tnls_kernel[0]; -+ tnls_kernel[0] = t; -+ kdev_count++; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+} -+ -+/** -+ * ip6ip6_kernel_tnl_unlink - remove first kernel tunnel from cache -+ * -+ * Return: first free kernel tunnel -+ * -+ * Note: -+ * See the comments on ip6ip6_kernel_tnl_add() for more information. -+ **/ -+ -+static inline struct ip6_tnl * -+ip6ip6_kernel_tnl_unlink(void) -+{ -+ struct ip6_tnl *t; -+ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ if ((t = tnls_kernel[0]) != NULL) { -+ tnls_kernel[0] = t->next; -+ kdev_count--; -+ } -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ return t; -+} -+ -+/** -+ * ip6ip6_tnl_link - add tunnel to hash table -+ * @t: tunnel to be added -+ **/ -+ -+static void -+ip6ip6_tnl_link(struct ip6_tnl *t) -+{ -+ struct ip6_tnl **tp = ip6ip6_bucket(&t->parms); -+ -+ write_lock_bh(&ip6ip6_lock); -+ t->next = *tp; -+ *tp = t; -+ write_unlock_bh(&ip6ip6_lock); -+} -+ -+/** -+ * ip6ip6_tnl_unlink - remove tunnel from hash table -+ * @t: tunnel to be removed -+ **/ -+ -+static void -+ip6ip6_tnl_unlink(struct ip6_tnl *t) -+{ -+ struct ip6_tnl **tp; -+ -+ write_lock_bh(&ip6ip6_lock); -+ for (tp = ip6ip6_bucket(&t->parms); *tp; tp = &(*tp)->next) { -+ if (t == *tp) { -+ *tp = t->next; -+ break; -+ } -+ } -+ write_unlock_bh(&ip6ip6_lock); -+} -+ -+/** -+ * ip6ip6_tnl_create() - create a new tunnel -+ * @p: tunnel parameters -+ * @pt: pointer to new tunnel -+ * -+ * Description: -+ * Create tunnel matching given parameters. New kernel managed devices are -+ * not put in the normal hash structure, but are instead cached for later -+ * use. -+ * -+ * Return: -+ * 0 on success -+ **/ -+ -+ -+static int __ip6ip6_tnl_create(struct ip6_tnl_parm *p, -+ struct ip6_tnl **pt, -+ int kernel_list) -+{ -+ struct net_device *dev; -+ int err = -ENOBUFS; -+ struct ip6_tnl *t; -+ -+ MOD_INC_USE_COUNT; -+ dev = kmalloc(sizeof (*dev) + sizeof (*t), GFP_KERNEL); -+ if (!dev) { -+ MOD_DEC_USE_COUNT; -+ return err; -+ } -+ memset(dev, 0, sizeof (*dev) + sizeof (*t)); -+ dev->priv = (void *) (dev + 1); -+ t = (struct ip6_tnl *) dev->priv; -+ t->dev = dev; -+ dev->init = ip6ip6_tnl_dev_init; -+ dev->features |= NETIF_F_DYNALLOC; -+ if (kernel_list) { -+ memcpy(t->parms.name, p->name, IFNAMSIZ - 1); -+ t->parms.proto = IPPROTO_IPV6; -+ t->parms.flags = IP6_TNL_F_KERNEL_DEV; -+ } else { -+ memcpy(&t->parms, p, sizeof (*p)); -+ } -+ t->parms.name[IFNAMSIZ - 1] = '\0'; -+ strcpy(dev->name, t->parms.name); -+ if (!dev->name[0]) { -+ int i; -+ for (i = 0; i < IP6_TNL_MAX; i++) { -+ sprintf(dev->name, "ip6tnl%d", i); -+ if (__dev_get_by_name(dev->name) == NULL) -+ break; -+ } -+ -+ if (i == IP6_TNL_MAX) { -+ goto failed; -+ } -+ memcpy(t->parms.name, dev->name, IFNAMSIZ); -+ } -+ if ((err = register_netdevice(dev)) < 0) { -+ goto failed; -+ } -+ dev_hold(dev); -+ if (kernel_list) { -+ ip6ip6_kernel_tnl_link(t); -+ } else { -+ ip6ip6_tnl_link(t); -+ } -+ *pt = t; -+ return 0; -+failed: -+ kfree(dev); -+ MOD_DEC_USE_COUNT; -+ return err; -+} -+ -+ -+int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) -+{ -+ return __ip6ip6_tnl_create(p, pt, 0); -+} -+ -+ -+static void manage_kernel_tnls(void *foo); -+ -+static struct tq_struct manager_task = { -+ routine:manage_kernel_tnls, -+ data:NULL -+}; -+ -+/** -+ * manage_kernel_tnls() - create and destroy kernel tunnels -+ * -+ * Description: -+ * manage_kernel_tnls() creates new kernel devices if there -+ * are less than $min_kdev_count of them and deletes old ones if -+ * there are less than $max_kdev_count of them in the cache -+ * -+ * Note: -+ * Schedules itself to be run later in process context if called from -+ * interrupt. Therefore only works synchronously when called from process -+ * context. -+ **/ -+ -+static void -+manage_kernel_tnls(void *foo) -+{ -+ struct ip6_tnl *t = NULL; -+ struct ip6_tnl_parm parm; -+ -+ /* We can't do this processing in interrupt -+ context so schedule it for later */ -+ if (in_interrupt()) { -+ read_lock(&ip6ip6_kernel_lock); -+ if (!shutdown && -+ (kdev_count < min_kdev_count || -+ kdev_count > max_kdev_count)) { -+ schedule_task(&manager_task); -+ } -+ read_unlock(&ip6ip6_kernel_lock); -+ return; -+ } -+ -+ rtnl_lock(); -+ read_lock_bh(&ip6ip6_kernel_lock); -+ memset(&parm, 0, sizeof (parm)); -+ parm.flags = IP6_TNL_F_KERNEL_DEV; -+ /* Create tunnels until there are at least min_kdev_count */ -+ while (kdev_count < min_kdev_count) { -+ read_unlock_bh(&ip6ip6_kernel_lock); -+ if (!__ip6ip6_tnl_create(&parm, &t, 1)) { -+ dev_open(t->dev); -+ } else { -+ goto err; -+ } -+ read_lock_bh(&ip6ip6_kernel_lock); -+ } -+ -+ /* Destroy tunnels until there are at most max_kdev_count */ -+ while (kdev_count > max_kdev_count) { -+ read_unlock_bh(&ip6ip6_kernel_lock); -+ if ((t = ip6ip6_kernel_tnl_unlink()) != NULL) { -+ unregister_netdevice(t->dev); -+ } else { -+ goto err; -+ } -+ read_lock_bh(&ip6ip6_kernel_lock); -+ } -+ read_unlock_bh(&ip6ip6_kernel_lock); -+err: -+ rtnl_unlock(); -+} -+ -+/** -+ * ip6ip6_tnl_inc_max_kdev_count() - increase max kernel dev cache size -+ * @n: size increase -+ * Description: -+ * Increase the upper limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_inc_max_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ max_kdev_count += n; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return max_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_dec_max_kdev_count() - decrease max kernel dev cache size -+ * @n: size decrement -+ * Description: -+ * Decrease the upper limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_dec_max_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ max_kdev_count -= min(max_kdev_count, n); -+ if (max_kdev_count < min_kdev_count) -+ min_kdev_count = max_kdev_count; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return max_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_inc_min_kdev_count() - increase min kernel dev cache size -+ * @n: size increase -+ * Description: -+ * Increase the lower limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_inc_min_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ min_kdev_count += n; -+ if (min_kdev_count > max_kdev_count) -+ max_kdev_count = min_kdev_count; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return min_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_dec_min_kdev_count() - decrease min kernel dev cache size -+ * @n: size decrement -+ * Description: -+ * Decrease the lower limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_dec_min_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ min_kdev_count -= min(min_kdev_count, n); -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return min_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_locate - find or create tunnel matching given parameters -+ * @p: tunnel parameters -+ * @create: != 0 if allowed to create new tunnel if no match found -+ * -+ * Description: -+ * ip6ip6_tnl_locate() first tries to locate an existing tunnel -+ * based on @parms. If this is unsuccessful, but @create is set a new -+ * tunnel device is created and registered for use. -+ * -+ * Return: -+ * 0 if tunnel located or created, -+ * -EINVAL if parameters incorrect, -+ * -ENODEV if no matching tunnel available -+ **/ -+ -+int ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create) -+{ -+ struct in6_addr *remote = &p->raddr; -+ struct in6_addr *local = &p->laddr; -+ struct ip6_tnl *t; -+ -+ if (p->proto != IPPROTO_IPV6) -+ return -EINVAL; -+ -+ for (t = *ip6ip6_bucket(p); t; t = t->next) { -+ if (!ipv6_addr_cmp(local, &t->parms.laddr) && -+ !ipv6_addr_cmp(remote, &t->parms.raddr)) { -+ *pt = t; -+ return (create ? -EEXIST : 0); -+ } -+ } -+ return ip6ip6_tnl_create(p, pt); -+} -+ -+/** -+ * ip6ip6_tnl_dev_destructor - tunnel device destructor -+ * @dev: the device to be destroyed -+ **/ -+ -+static void -+ip6ip6_tnl_dev_destructor(struct net_device *dev) -+{ -+ if (dev != &ip6ip6_fb_tnl_dev) { -+ MOD_DEC_USE_COUNT; -+ } -+} -+ -+/** -+ * ip6ip6_tnl_dev_uninit - tunnel device uninitializer -+ * @dev: the device to be destroyed -+ * -+ * Description: -+ * ip6ip6_tnl_dev_uninit() removes tunnel from its list -+ **/ -+ -+static void -+ip6ip6_tnl_dev_uninit(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ -+ if (dev == &ip6ip6_fb_tnl_dev) { -+ write_lock_bh(&ip6ip6_lock); -+ tnls_wc[0] = NULL; -+ write_unlock_bh(&ip6ip6_lock); -+ } else { -+ ip6ip6_tnl_unlink(t); -+ } -+ sock_release(t->sock); -+ dev_put(dev); -+} -+ -+/** -+ * parse_tvl_tnl_enc_lim - handle encapsulation limit option -+ * @skb: received socket buffer -+ * -+ * Return: -+ * 0 if none was found, -+ * else index to encapsulation limit -+ **/ -+ -+static __u16 -+parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) -+{ -+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw; -+ __u8 nexthdr = ipv6h->nexthdr; -+ __u16 off = sizeof (*ipv6h); -+ -+ while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { -+ __u16 optlen = 0; -+ struct ipv6_opt_hdr *hdr; -+ if (raw + off + sizeof (*hdr) > skb->data && -+ !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) -+ break; -+ -+ hdr = (struct ipv6_opt_hdr *) (raw + off); -+ if (nexthdr == NEXTHDR_FRAGMENT) { -+ struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; -+ if (frag_hdr->frag_off) -+ break; -+ optlen = 8; -+ } else if (nexthdr == NEXTHDR_AUTH) { -+ optlen = (hdr->hdrlen + 2) << 2; -+ } else { -+ optlen = ipv6_optlen(hdr); -+ } -+ if (nexthdr == NEXTHDR_DEST) { -+ __u16 i = off + 2; -+ while (1) { -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ -+ /* No more room for encapsulation limit */ -+ if (i + sizeof (*tel) > off + optlen) -+ break; -+ -+ tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; -+ /* return index of option if found and valid */ -+ if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && -+ tel->length == 1) -+ return i; -+ /* else jump to next option */ -+ if (tel->type) -+ i += tel->length + 2; -+ else -+ i++; -+ } -+ } -+ nexthdr = hdr->nexthdr; -+ off += optlen; -+ } -+ return 0; -+} -+ -+/** -+ * ip6ip6_err - tunnel error handler -+ * -+ * Description: -+ * ip6ip6_err() should handle errors in the tunnel according -+ * to the specifications in RFC 2473. -+ **/ -+ -+void ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, -+ int type, int code, int offset, __u32 info) -+{ -+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; -+ struct ip6_tnl *t; -+ int rel_msg = 0; -+ int rel_type = ICMPV6_DEST_UNREACH; -+ int rel_code = ICMPV6_ADDR_UNREACH; -+ __u32 rel_info = 0; -+ __u16 len; -+ -+ /* If the packet doesn't contain the original IPv6 header we are -+ in trouble since we might need the source address for furter -+ processing of the error. */ -+ -+ read_lock(&ip6ip6_lock); -+ if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL) -+ goto out; -+ -+ switch (type) { -+ __u32 teli; -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ __u32 mtu; -+ case ICMPV6_DEST_UNREACH: -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Path to destination invalid " -+ "or inactive!\n", t->parms.name); -+ rel_msg = 1; -+ break; -+ case ICMPV6_TIME_EXCEED: -+ if (code == ICMPV6_EXC_HOPLIMIT) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Too small hop limit or " -+ "routing loop in tunnel!\n", -+ t->parms.name); -+ rel_msg = 1; -+ } -+ break; -+ case ICMPV6_PARAMPROB: -+ /* ignore if parameter problem not caused by a tunnel -+ encapsulation limit sub-option */ -+ if (code != ICMPV6_HDR_FIELD) { -+ break; -+ } -+ teli = parse_tlv_tnl_enc_lim(skb, skb->data); -+ -+ if (teli && teli == ntohl(info) - 2) { -+ tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; -+ if (tel->encap_limit == 0) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Too small encapsulation " -+ "limit or routing loop in " -+ "tunnel!\n", t->parms.name); -+ rel_msg = 1; -+ } -+ } -+ break; -+ case ICMPV6_PKT_TOOBIG: -+ mtu = ntohl(info) - offset; -+ if (mtu < IPV6_MIN_MTU) -+ mtu = IPV6_MIN_MTU; -+ t->dev->mtu = mtu; -+ -+ if ((len = sizeof (*ipv6h) + ipv6h->payload_len) > mtu) { -+ rel_type = ICMPV6_PKT_TOOBIG; -+ rel_code = 0; -+ rel_info = mtu; -+ rel_msg = 1; -+ } -+ break; -+ } -+ if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { -+ struct rt6_info *rt; -+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); -+ if (!skb2) -+ goto out; -+ -+ dst_release(skb2->dst); -+ skb2->dst = NULL; -+ skb_pull(skb2, offset); -+ skb2->nh.raw = skb2->data; -+ -+ /* Try to guess incoming interface */ -+ rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0); -+ -+ if (rt && rt->rt6i_dev) -+ skb2->dev = rt->rt6i_dev; -+ -+ icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev); -+ -+ if (rt) -+ dst_release(&rt->u.dst); -+ -+ kfree_skb(skb2); -+ } -+out: -+ read_unlock(&ip6ip6_lock); -+} -+ -+/** -+ * call_hooks - call ipv6 tunnel hooks -+ * @hooknum: hook number, either %IP6_TNL_PRE_ENCAP, or -+ * %IP6_TNL_PRE_DECAP -+ * @t: the current tunnel -+ * @skb: the tunneled packet -+ * -+ * Description: -+ * Pass packet to all the hook functions until %IP6_TNL_DROP -+ * -+ * Return: -+ * %IP6_TNL_ACCEPT or %IP6_TNL_DROP -+ **/ -+ -+static inline int -+call_hooks(unsigned int hooknum, struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ struct ip6_tnl_hook_ops *h; -+ int accept = IP6_TNL_ACCEPT; -+ -+ if (hooknum < IP6_TNL_MAXHOOKS) { -+ struct list_head *i; -+ read_lock(&ip6ip6_hook_lock); -+ for (i = hooks[hooknum].next; i != &hooks[hooknum]; i = i->next) { -+ h = (struct ip6_tnl_hook_ops *) i; -+ -+ if (h->hook) { -+ accept = h->hook(t, skb); -+ -+ if (accept != IP6_TNL_ACCEPT) -+ break; -+ } -+ } -+ read_unlock(&ip6ip6_hook_lock); -+ } -+ return accept; -+} -+ -+/** -+ * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally -+ * @skb: received socket buffer -+ * -+ * Return: 0 -+ **/ -+ -+int ip6ip6_rcv(struct sk_buff *skb) -+{ -+ struct ipv6hdr *ipv6h; -+ struct ip6_tnl *t; -+ -+ if (!pskb_may_pull(skb, sizeof (*ipv6h))) -+ goto discard; -+ -+ ipv6h = skb->nh.ipv6h; -+ -+ read_lock(&ip6ip6_lock); -+ -+ if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { -+ if (!(t->parms.flags & IP6_TNL_F_CAP_RCV) || -+ call_hooks(IP6_TNL_PRE_DECAP, t, skb) != IP6_TNL_ACCEPT) { -+ t->stat.rx_dropped++; -+ read_unlock(&ip6ip6_lock); -+ goto discard; -+ } -+ skb->mac.raw = skb->nh.raw; -+ skb->nh.raw = skb->data; -+ skb->protocol = htons(ETH_P_IPV6); -+ skb->pkt_type = PACKET_HOST; -+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); -+ skb->dev = t->dev; -+ dst_release(skb->dst); -+ skb->dst = NULL; -+ t->stat.rx_packets++; -+ t->stat.rx_bytes += skb->len; -+ netif_rx(skb); -+ read_unlock(&ip6ip6_lock); -+ return 0; -+ } -+ read_unlock(&ip6ip6_lock); -+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); -+discard: -+ kfree_skb(skb); -+ return 0; -+} -+ -+static inline struct ipv6_txoptions *create_tel(__u8 encap_limit) -+{ -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ struct ipv6_txoptions *opt; -+ __u8 *raw; -+ -+ int opt_len = sizeof(*opt) + IPV6_TLV_TEL_DST_SIZE; -+ -+ if (!(opt = kmalloc(opt_len, GFP_ATOMIC))) { -+ return NULL; -+ } -+ memset(opt, 0, opt_len); -+ opt->tot_len = opt_len; -+ opt->dst0opt = (struct ipv6_opt_hdr *) (opt + 1); -+ opt->opt_nflen = 8; -+ -+ tel = (struct ipv6_tlv_tnl_enc_lim *) (opt->dst0opt + 1); -+ tel->type = IPV6_TLV_TNL_ENCAP_LIMIT; -+ tel->length = 1; -+ tel->encap_limit = encap_limit; -+ -+ raw = (__u8 *) opt->dst0opt; -+ raw[5] = IPV6_TLV_PADN; -+ raw[6] = 1; -+ -+ return opt; -+} -+ -+static int -+ip6ip6_getfrag(const void *data, struct in6_addr *addr, -+ char *buff, unsigned int offset, unsigned int len) -+{ -+ memcpy(buff, data + offset, len); -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_addr_conflict - compare packet addresses to tunnel's own -+ * @t: the outgoing tunnel device -+ * @hdr: IPv6 header from the incoming packet -+ * -+ * Description: -+ * Avoid trivial tunneling loop by checking that tunnel exit-point -+ * doesn't match source of incoming packet. -+ * -+ * Return: -+ * 1 if conflict, -+ * 0 else -+ **/ -+ -+static inline int -+ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) -+{ -+ return !ipv6_addr_cmp(&t->parms.raddr, &hdr->saddr); -+} -+ -+/** -+ * ip6ip6_tnl_xmit - encapsulate packet and send -+ * @skb: the outgoing socket buffer -+ * @dev: the outgoing tunnel device -+ * -+ * Description: -+ * Build new header and do some sanity checks on the packet before sending -+ * it to ip6_build_xmit(). -+ * -+ * Return: -+ * 0 -+ **/ -+ -+int ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ struct net_device_stats *stats = &t->stat; -+ struct ipv6hdr *ipv6h = skb->nh.ipv6h; -+ struct ipv6_txoptions *opt = NULL; -+ int encap_limit = -1; -+ __u16 offset; -+ struct flowi fl; -+ int err = 0; -+ struct dst_entry *dst; -+ struct sock *sk = t->sock->sk; -+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; -+ int mtu; -+ -+ if (t->recursion++) { -+ stats->collisions++; -+ goto tx_err; -+ } -+ if (skb->protocol != htons(ETH_P_IPV6) || -+ !(t->parms.flags & IP6_TNL_F_CAP_XMIT) || -+ ip6ip6_tnl_addr_conflict(t, ipv6h)) { -+ goto tx_err; -+ } -+ if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) { -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset]; -+ if (tel->encap_limit == 0) { -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, offset + 2, skb->dev); -+ goto tx_err; -+ } -+ encap_limit = tel->encap_limit - 1; -+ } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { -+ encap_limit = t->parms.encap_limit; -+ } -+ if (call_hooks(IP6_TNL_PRE_ENCAP, t, skb) != IP6_TNL_ACCEPT) -+ goto discard; -+ memcpy(&fl, &t->fl, sizeof (fl)); -+ -+ if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) -+ fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_TCLASS_MASK); -+ if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) -+ fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_FLOWLABEL_MASK); -+ -+ if (encap_limit >= 0 && (opt = create_tel(encap_limit)) == NULL) -+ goto tx_err; -+ -+ dst = __sk_dst_check(sk, np->dst_cookie); -+ -+ if (dst) { -+ if (np->daddr_cache == NULL || -+ ipv6_addr_cmp(fl.fl6_dst, np->daddr_cache) || -+#ifdef CONFIG_IPV6_SUBTREES -+ np->saddr_cache == NULL || -+ ipv6_addr_cmp(fl.fl6_src, np->saddr_cache) || -+#endif -+ (fl.oif && fl.oif != dst->dev->ifindex)) { -+ dst = NULL; -+ } else { -+ dst_hold(dst); -+ } -+ } -+ if (dst == NULL) { -+ dst = ip6_route_output(sk, &fl); -+ if (dst->error) { -+ stats->tx_carrier_errors++; -+ dst_link_failure(skb); -+ goto tx_err_dst_release; -+ } -+ /* local routing loop */ -+ if (dst->dev == dev) { -+ stats->collisions++; -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Local routing loop detected!\n", -+ t->parms.name); -+ goto tx_err_dst_release; -+ } -+ } -+ mtu = dst->pmtu - sizeof (*ipv6h); -+ if (opt) { -+ mtu -= (opt->opt_nflen + opt->opt_flen); -+ } -+ if (mtu < IPV6_MIN_MTU) -+ mtu = IPV6_MIN_MTU; -+ if (skb->dst && mtu < skb->dst->pmtu) { -+ struct rt6_info *rt = (struct rt6_info *) skb->dst; -+ rt->rt6i_flags |= RTF_MODIFIED; -+ rt->u.dst.pmtu = mtu; -+ } -+ if (skb->len > mtu) { -+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); -+ goto tx_err_dst_release; -+ } -+ ip6_dst_store(sk, dst, &np->daddr, &np->saddr); -+ err = ip6_build_xmit(sk, ip6ip6_getfrag, (void *) skb->nh.raw, -+ &fl, skb->len, opt, t->parms.hop_limit, -+ MSG_DONTWAIT); -+ -+ if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) { -+ stats->tx_bytes += skb->len; -+ stats->tx_packets++; -+ } else { -+ stats->tx_errors++; -+ stats->tx_aborted_errors++; -+ } -+ if (opt) -+ kfree(opt); -+ kfree_skb(skb); -+ t->recursion--; -+ return 0; -+tx_err_dst_release: -+ dst_release(dst); -+ if (opt) -+ kfree(opt); -+tx_err: -+ stats->tx_errors++; -+discard: -+ stats->tx_dropped++; -+ kfree_skb(skb); -+ t->recursion--; -+ return 0; -+} -+ -+static void ip6_tnl_set_cap(struct ip6_tnl *t) -+{ -+ struct ip6_tnl_parm *p = &t->parms; -+ struct in6_addr *laddr = &p->laddr; -+ struct in6_addr *raddr = &p->raddr; -+ int ltype = ipv6_addr_type(laddr); -+ int rtype = ipv6_addr_type(raddr); -+ -+ p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV); -+ -+ if (ltype != IPV6_ADDR_ANY && rtype != IPV6_ADDR_ANY && -+ ((ltype|rtype) & -+ (IPV6_ADDR_UNICAST| -+ IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL| -+ IPV6_ADDR_MAPPED|IPV6_ADDR_RESERVED)) == IPV6_ADDR_UNICAST) { -+ struct net_device *ldev = NULL; -+ int l_ok = 1; -+ int r_ok = 1; -+ -+ if (p->link) -+ ldev = dev_get_by_index(p->link); -+ -+ if ((ltype&IPV6_ADDR_UNICAST) && !ipv6_chk_addr(laddr, ldev)) -+ l_ok = 0; -+ -+ if ((rtype&IPV6_ADDR_UNICAST) && ipv6_chk_addr(raddr, NULL)) -+ r_ok = 0; -+ -+ if (l_ok && r_ok) { -+ if (ltype&IPV6_ADDR_UNICAST) -+ p->flags |= IP6_TNL_F_CAP_XMIT; -+ if (rtype&IPV6_ADDR_UNICAST) -+ p->flags |= IP6_TNL_F_CAP_RCV; -+ } -+ if (ldev) -+ dev_put(ldev); -+ } -+} -+ -+static void ip6ip6_tnl_link_config(struct ip6_tnl *t) -+{ -+ struct net_device *dev = t->dev; -+ struct ip6_tnl_parm *p = &t->parms; -+ struct flowi *fl = &t->fl; -+ -+ /* Set up flowi template */ -+ fl->fl6_src = &p->laddr; -+ fl->fl6_dst = &p->raddr; -+ fl->oif = p->link; -+ fl->fl6_flowlabel = 0; -+ -+ if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS)) -+ fl->fl6_flowlabel |= IPV6_TCLASS_MASK & htonl(p->flowinfo); -+ if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) -+ fl->fl6_flowlabel |= IPV6_FLOWLABEL_MASK & htonl(p->flowinfo); -+ -+ ip6_tnl_set_cap(t); -+ -+ if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV) -+ dev->flags |= IFF_POINTOPOINT; -+ else -+ dev->flags &= ~IFF_POINTOPOINT; -+ -+ if (p->flags & IP6_TNL_F_CAP_XMIT) { -+ struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr, -+ p->link, 0); -+ -+ if (rt == NULL) -+ return; -+ -+ if (rt->rt6i_dev) { -+ dev->iflink = rt->rt6i_dev->ifindex; -+ -+ dev->hard_header_len = rt->rt6i_dev->hard_header_len + -+ sizeof (struct ipv6hdr); -+ -+ dev->mtu = rt->rt6i_dev->mtu - sizeof (struct ipv6hdr); -+ -+ if (dev->mtu < IPV6_MIN_MTU) -+ dev->mtu = IPV6_MIN_MTU; -+ } -+ dst_release(&rt->u.dst); -+ } -+} -+ -+/** -+ * __ip6ip6_tnl_change - update the tunnel parameters -+ * @t: tunnel to be changed -+ * @p: tunnel configuration parameters -+ * -+ * Description: -+ * __ip6ip6_tnl_change() updates the tunnel parameters -+ **/ -+ -+static void -+__ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) -+{ -+ ipv6_addr_copy(&t->parms.laddr, &p->laddr); -+ ipv6_addr_copy(&t->parms.raddr, &p->raddr); -+ t->parms.flags = p->flags; -+ t->parms.hop_limit = p->hop_limit; -+ t->parms.encap_limit = p->encap_limit; -+ t->parms.flowinfo = p->flowinfo; -+ ip6ip6_tnl_link_config(t); -+} -+ -+void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) -+{ -+ ip6ip6_tnl_unlink(t); -+ __ip6ip6_tnl_change(t, p); -+ ip6ip6_tnl_link(t); -+} -+ -+/** -+ * ip6ip6_kernel_tnl_add - configure and add kernel tunnel to hash -+ * @p: kernel tunnel configuration parameters -+ * -+ * Description: -+ * ip6ip6_kernel_tnl_add() fetches an unused kernel tunnel configures -+ * it according to @p and places it among the active tunnels. -+ * -+ * Return: -+ * number of references to tunnel on success, -+ * %-EEXIST if there is already a device matching description -+ * %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised, -+ * %-ENODEV if there are no unused kernel tunnels available -+ * -+ * Note: -+ * The code for creating, opening, closing and destroying network devices -+ * must be called from process context, while the Mobile IP code, which -+ * needs the tunnel devices, unfortunately runs in interrupt context. -+ * -+ * The devices must be created and opened in advance, then placed in a -+ * list where the kernel can fetch and ready them for use at a later time. -+ * -+ **/ -+ -+int -+ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p) -+{ -+ struct ip6_tnl *t; -+ -+ if (!(p->flags & IP6_TNL_F_KERNEL_DEV)) -+ return -EINVAL; -+ if ((t = ip6ip6_tnl_lookup(&p->raddr, &p->laddr)) != NULL && -+ t != &ip6ip6_fb_tnl) { -+ /* Handle duplicate tunnels by incrementing -+ reference count */ -+ atomic_inc(&t->refcnt); -+ goto out; -+ } -+ if ((t = ip6ip6_kernel_tnl_unlink()) == NULL) -+ return -ENODEV; -+ __ip6ip6_tnl_change(t, p); -+ -+ atomic_inc(&t->refcnt); -+ -+ ip6ip6_tnl_link(t); -+ -+ manage_kernel_tnls(NULL); -+out: -+ return atomic_read(&t->refcnt); -+} -+ -+/** -+ * ip6ip6_kernel_tnl_del - delete no longer needed kernel tunnel -+ * @t: kernel tunnel to be removed from hash -+ * -+ * Description: -+ * ip6ip6_kernel_tnl_del() removes and deconfigures the tunnel @t -+ * and places it among the unused kernel devices. -+ * -+ * Return: -+ * number of references on success, -+ * %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised, -+ * -+ * Note: -+ * See the comments on ip6ip6_kernel_tnl_add() for more information. -+ **/ -+ -+int -+ip6ip6_kernel_tnl_del(struct ip6_tnl *t) -+{ -+ if (!t) -+ return -ENODEV; -+ -+ if (!(t->parms.flags & IP6_TNL_F_KERNEL_DEV)) -+ return -EINVAL; -+ -+ if (atomic_dec_and_test(&t->refcnt)) { -+ struct ip6_tnl_parm p; -+ ip6ip6_tnl_unlink(t); -+ memset(&p, 0, sizeof (p)); -+ p.flags = IP6_TNL_F_KERNEL_DEV; -+ -+ __ip6ip6_tnl_change(t, &p); -+ -+ ip6ip6_kernel_tnl_link(t); -+ -+ manage_kernel_tnls(NULL); -+ } -+ return atomic_read(&t->refcnt); -+} -+ -+/** -+ * ip6ip6_tnl_ioctl - configure ipv6 tunnels from userspace -+ * @dev: virtual device associated with tunnel -+ * @ifr: parameters passed from userspace -+ * @cmd: command to be performed -+ * -+ * Description: -+ * ip6ip6_tnl_ioctl() is used for managing IPv6 tunnels -+ * from userspace. -+ * -+ * The possible commands are the following: -+ * %SIOCGETTUNNEL: get tunnel parameters for device -+ * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters -+ * %SIOCCHGTUNNEL: change tunnel parameters to those given -+ * %SIOCDELTUNNEL: delete tunnel -+ * -+ * The fallback device "ip6tnl0", created during module -+ * initialization, can be used for creating other tunnel devices. -+ * -+ * Return: -+ * 0 on success, -+ * %-EFAULT if unable to copy data to or from userspace, -+ * %-EPERM if current process hasn't %CAP_NET_ADMIN set or attempting -+ * to configure kernel devices from userspace, -+ * %-EINVAL if passed tunnel parameters are invalid, -+ * %-EEXIST if changing a tunnel's parameters would cause a conflict -+ * %-ENODEV if attempting to change or delete a nonexisting device -+ * -+ * Note: -+ * See the comments on ip6ip6_kernel_tnl_add() for more information -+ * about kernel tunnels. -+ * **/ -+ -+static int -+ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -+{ -+ int err = 0; -+ int create; -+ struct ip6_tnl_parm p; -+ struct ip6_tnl *t = NULL; -+ -+ MOD_INC_USE_COUNT; -+ -+ switch (cmd) { -+ case SIOCGETTUNNEL: -+ if (dev == &ip6ip6_fb_tnl_dev) { -+ if (copy_from_user(&p, -+ ifr->ifr_ifru.ifru_data, -+ sizeof (p))) { -+ err = -EFAULT; -+ break; -+ } -+ if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV) -+ t = (struct ip6_tnl *) dev->priv; -+ else if (err) -+ break; -+ } else -+ t = (struct ip6_tnl *) dev->priv; -+ -+ memcpy(&p, &t->parms, sizeof (p)); -+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { -+ err = -EFAULT; -+ } -+ break; -+ case SIOCADDTUNNEL: -+ case SIOCCHGTUNNEL: -+ err = -EPERM; -+ create = (cmd == SIOCADDTUNNEL); -+ if (!capable(CAP_NET_ADMIN)) -+ break; -+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { -+ err = -EFAULT; -+ break; -+ } -+ if (p.flags & IP6_TNL_F_KERNEL_DEV) { -+ break; -+ } -+ if (!create && dev != &ip6ip6_fb_tnl_dev) { -+ t = (struct ip6_tnl *) dev->priv; -+ } -+ if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) { -+ break; -+ } -+ if (cmd == SIOCCHGTUNNEL) { -+ if (t->dev != dev) { -+ err = -EEXIST; -+ break; -+ } -+ if (t->parms.flags & IP6_TNL_F_KERNEL_DEV) { -+ err = -EPERM; -+ break; -+ } -+ ip6ip6_tnl_change(t, &p); -+ netdev_state_change(dev); -+ } -+ if (copy_to_user(ifr->ifr_ifru.ifru_data, -+ &t->parms, sizeof (p))) { -+ err = -EFAULT; -+ } else { -+ err = 0; -+ } -+ break; -+ case SIOCDELTUNNEL: -+ err = -EPERM; -+ if (!capable(CAP_NET_ADMIN)) -+ break; -+ -+ if (dev == &ip6ip6_fb_tnl_dev) { -+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, -+ sizeof (p))) { -+ err = -EFAULT; -+ break; -+ } -+ err = ip6ip6_tnl_locate(&p, &t, 0); -+ if (err) -+ break; -+ if (t == &ip6ip6_fb_tnl) { -+ err = -EPERM; -+ break; -+ } -+ } else { -+ t = (struct ip6_tnl *) dev->priv; -+ } -+ if (t->parms.flags & IP6_TNL_F_KERNEL_DEV) -+ err = -EPERM; -+ else -+ err = unregister_netdevice(t->dev); -+ break; -+ default: -+ err = -EINVAL; -+ } -+ MOD_DEC_USE_COUNT; -+ return err; -+} -+ -+/** -+ * ip6ip6_tnl_get_stats - return the stats for tunnel device -+ * @dev: virtual device associated with tunnel -+ * -+ * Return: stats for device -+ **/ -+ -+static struct net_device_stats * -+ip6ip6_tnl_get_stats(struct net_device *dev) -+{ -+ return &(((struct ip6_tnl *) dev->priv)->stat); -+} -+ -+/** -+ * ip6ip6_tnl_change_mtu - change mtu manually for tunnel device -+ * @dev: virtual device associated with tunnel -+ * @new_mtu: the new mtu -+ * -+ * Return: -+ * 0 on success, -+ * %-EINVAL if mtu too small -+ **/ -+ -+static int -+ip6ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) -+{ -+ if (new_mtu < IPV6_MIN_MTU) { -+ return -EINVAL; -+ } -+ dev->mtu = new_mtu; -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_dev_init_gen - general initializer for all tunnel devices -+ * @dev: virtual device associated with tunnel -+ * -+ * Description: -+ * Set function pointers and initialize the &struct flowi template used -+ * by the tunnel. -+ **/ -+ -+static int -+ip6ip6_tnl_dev_init_gen(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ struct flowi *fl = &t->fl; -+ int err; -+ struct sock *sk; -+ -+ if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_IPV6, &t->sock))) { -+ printk(KERN_ERR -+ "Failed to create IPv6 tunnel socket (err %d).\n", err); -+ return err; -+ } -+ t->sock->inode->i_uid = 0; -+ t->sock->inode->i_gid = 0; -+ -+ sk = t->sock->sk; -+ sk->allocation = GFP_ATOMIC; -+ sk->net_pinfo.af_inet6.hop_limit = 254; -+ sk->net_pinfo.af_inet6.mc_loop = 0; -+ sk->prot->unhash(sk); -+ -+ memset(fl, 0, sizeof (*fl)); -+ fl->proto = IPPROTO_IPV6; -+ -+ dev->destructor = ip6ip6_tnl_dev_destructor; -+ dev->uninit = ip6ip6_tnl_dev_uninit; -+ dev->hard_start_xmit = ip6ip6_tnl_xmit; -+ dev->get_stats = ip6ip6_tnl_get_stats; -+ dev->do_ioctl = ip6ip6_tnl_ioctl; -+ dev->change_mtu = ip6ip6_tnl_change_mtu; -+ -+ dev->type = ARPHRD_TUNNEL6; -+ dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr); -+ dev->mtu = ETH_DATA_LEN - sizeof (struct ipv6hdr); -+ dev->flags |= IFF_NOARP; -+ dev->iflink = 0; -+ /* Hmm... MAX_ADDR_LEN is 8, so the ipv6 addresses can't be -+ copied to dev->dev_addr and dev->broadcast, like the ipv4 -+ addresses were in ipip.c, ip_gre.c and sit.c. */ -+ dev->addr_len = 0; -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_dev_init - initializer for all non fallback tunnel devices -+ * @dev: virtual device associated with tunnel -+ **/ -+ -+static int -+ip6ip6_tnl_dev_init(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ ip6ip6_tnl_dev_init_gen(dev); -+ ip6ip6_tnl_link_config(t); -+ return 0; -+} -+ -+#ifdef MODULE -+ -+/** -+ * ip6ip6_fb_tnl_open - function called when fallback device opened -+ * @dev: fallback device -+ * -+ * Return: 0 -+ **/ -+ -+static int -+ip6ip6_fb_tnl_open(struct net_device *dev) -+{ -+ MOD_INC_USE_COUNT; -+ return 0; -+} -+ -+/** -+ * ip6ip6_fb_tnl_close - function called when fallback device closed -+ * @dev: fallback device -+ * -+ * Return: 0 -+ **/ -+ -+static int -+ip6ip6_fb_tnl_close(struct net_device *dev) -+{ -+ MOD_DEC_USE_COUNT; -+ return 0; -+} -+#endif -+ -+/** -+ * ip6ip6_fb_tnl_dev_init - initializer for fallback tunnel device -+ * @dev: fallback device -+ * -+ * Return: 0 -+ **/ -+ -+int __init -+ip6ip6_fb_tnl_dev_init(struct net_device *dev) -+{ -+ ip6ip6_tnl_dev_init_gen(dev); -+#ifdef MODULE -+ dev->open = ip6ip6_fb_tnl_open; -+ dev->stop = ip6ip6_fb_tnl_close; -+#endif -+ dev_hold(dev); -+ tnls_wc[0] = &ip6ip6_fb_tnl; -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_register_hook - add hook for processing of tunneled packets -+ * @reg: hook function and its parameters -+ * -+ * Description: -+ * Add a netfilter like hook function for special handling of tunneled -+ * packets. The hook functions are called before encapsulation -+ * (%IP6_TNL_PRE_ENCAP) and before decapsulation -+ * (%IP6_TNL_PRE_DECAP). The possible return values by the hook -+ * functions are %IP6_TNL_DROP, %IP6_TNL_ACCEPT and -+ * %IP6_TNL_STOLEN (in case the hook function took care of the packet -+ * and it doesn't have to be processed any further). -+ **/ -+ -+void -+ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg) -+{ -+ if (reg->hooknum < IP6_TNL_MAXHOOKS) { -+ struct list_head *i; -+ -+ write_lock_bh(&ip6ip6_hook_lock); -+ for (i = hooks[reg->hooknum].next; -+ i != &hooks[reg->hooknum]; i = i->next) { -+ if (reg->priority < -+ ((struct ip6_tnl_hook_ops *) i)->priority) { -+ break; -+ } -+ } -+ list_add(®->list, i->prev); -+ write_unlock_bh(&ip6ip6_hook_lock); -+ } -+} -+ -+/** -+ * ip6ip6_tnl_unregister_hook - remove tunnel hook -+ * @reg: hook function and its parameters -+ **/ -+ -+void -+ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg) -+{ -+ if (reg->hooknum < IP6_TNL_MAXHOOKS) { -+ write_lock_bh(&ip6ip6_hook_lock); -+ list_del(®->list); -+ write_unlock_bh(&ip6ip6_hook_lock); -+ } -+} -+ -+ -+/* the IPv6 over IPv6 protocol structure */ -+static struct inet6_protocol ip6ip6_protocol = { -+ ip6ip6_rcv, /* IPv6 handler */ -+ ip6ip6_err, /* IPv6 error control */ -+ NULL, /* next */ -+ IPPROTO_IPV6, /* protocol ID */ -+ 0, /* copy */ -+ NULL, /* data */ -+ "IPv6 over IPv6" /* name */ -+}; -+ -+/** -+ * ip6_tunnel_init - register protocol and reserve needed resources -+ * -+ * Return: 0 on success -+ **/ -+ -+int __init ip6_tunnel_init(void) -+{ -+ int i, err; -+ -+ ip6ip6_fb_tnl_dev.priv = (void *) &ip6ip6_fb_tnl; -+ -+ for (i = 0; i < IP6_TNL_MAXHOOKS; i++) { -+ INIT_LIST_HEAD(&hooks[i]); -+ } -+ if ((err = register_netdev(&ip6ip6_fb_tnl_dev))) -+ return err; -+ -+ inet6_add_protocol(&ip6ip6_protocol); -+ return 0; -+} -+ -+/** -+ * ip6_tunnel_cleanup - free resources and unregister protocol -+ **/ -+ -+void ip6_tunnel_cleanup(void) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ shutdown = 1; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ flush_scheduled_tasks(); -+ manage_kernel_tnls(NULL); -+ inet6_del_protocol(&ip6ip6_protocol); -+ unregister_netdev(&ip6ip6_fb_tnl_dev); -+} -+ -+#ifdef MODULE -+module_init(ip6_tunnel_init); -+module_exit(ip6_tunnel_cleanup); -+#endif -+ -+#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE) -+EXPORT_SYMBOL(ip6ip6_tnl_register_hook); -+EXPORT_SYMBOL(ip6ip6_tnl_unregister_hook); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE -+EXPORT_SYMBOL(ip6ip6_tnl_dec_max_kdev_count); -+EXPORT_SYMBOL(ip6ip6_tnl_inc_max_kdev_count); -+EXPORT_SYMBOL(ip6ip6_tnl_dec_min_kdev_count); -+EXPORT_SYMBOL(ip6ip6_tnl_inc_min_kdev_count); -+EXPORT_SYMBOL(ip6ip6_kernel_tnl_add); -+EXPORT_SYMBOL(ip6ip6_kernel_tnl_del); -+EXPORT_SYMBOL(ip6ip6_tnl_lookup); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE -+EXPORT_SYMBOL(ip6ip6_tnl_create); -+EXPORT_SYMBOL(ip6ip6_tnl_change); -+#endif -+ -diff -uprN linux-2.4.25.old/net/ipv6/mipglue.c linux-2.4.25/net/ipv6/mipglue.c ---- linux-2.4.25.old/net/ipv6/mipglue.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mipglue.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,63 @@ -+/* -+ * Glue for Mobility support integration to IPv6 -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/sched.h> -+ -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/neighbour.h> -+#include <net/mipglue.h> -+ -+extern int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff); -+ -+/* Initialize all zero */ -+struct mipv6_callable_functions mipv6_functions = { NULL }; -+ -+/* Sets mipv6_functions struct to zero to invalidate all successive -+ * calls to mipv6 functions. Used on module unload. */ -+ -+void mipv6_invalidate_calls(void) -+{ -+ memset(&mipv6_functions, 0, sizeof(mipv6_functions)); -+} -+ -+ -+/* Selects correct handler for tlv encoded destination option. Called -+ * by ip6_parse_tlv. Checks if mipv6 calls are valid before calling. */ -+ -+int mipv6_handle_dstopt(struct sk_buff *skb, int optoff) -+{ -+ int ret; -+ -+ switch (skb->nh.raw[optoff]) { -+ case MIPV6_TLV_HOMEADDR: -+ ret = MIPV6_CALLFUNC(mipv6_handle_homeaddr, 0)(skb, optoff); -+ break; -+ default: -+ /* Should never happen */ -+ printk(KERN_ERR __FILE__ ": Invalid destination option code (%d)\n", -+ skb->nh.raw[optoff]); -+ ret = 1; -+ break; -+ } -+ -+ /* If mipv6 handlers are not valid, pass the packet to -+ * ip6_tlvopt_unknown() for correct handling. */ -+ if (!ret) -+ return ip6_tlvopt_unknown(skb, optoff); -+ -+ return ret; -+} -+ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/Config.in linux-2.4.25/net/ipv6/mobile_ip6/Config.in ---- linux-2.4.25.old/net/ipv6/mobile_ip6/Config.in 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/Config.in 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,12 @@ -+# -+# Mobile IPv6 Configuration -+# -+dep_tristate ' IPv6: Mobility Support (Correspondent Node)' CONFIG_IPV6_MOBILITY $CONFIG_IPV6 -+if [ "$CONFIG_IPV6_IPV6_TUNNEL" != "n" ]; then -+ dep_tristate ' MIPv6: Mobile Node Support' CONFIG_IPV6_MOBILITY_MN $CONFIG_IPV6_MOBILITY -+ -+ dep_tristate ' MIPv6: Home Agent Support' CONFIG_IPV6_MOBILITY_HA $CONFIG_IPV6_MOBILITY -+fi -+if [ "$CONFIG_IPV6_MOBILITY" != "n" ]; then -+ bool ' MIPv6: Debug messages' CONFIG_IPV6_MOBILITY_DEBUG -+fi -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/Makefile linux-2.4.25/net/ipv6/mobile_ip6/Makefile ---- linux-2.4.25.old/net/ipv6/mobile_ip6/Makefile 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/Makefile 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,35 @@ -+# -+# Makefile for the MIPL Mobile IPv6 for Linux. -+# -+# Note! Dependencies are done automagically by 'make dep', which also -+# removes any old dependencies. DON'T put your own dependencies here -+# unless it's something special (ie not a .c file). -+# -+ -+ -+O_TARGET := mip6_base.o -+ -+list-multi := mip6_ha.o mip6_mn.o -+ -+obj-y := hashlist.o bcache.o mobhdr_common.o stats.o exthdrs.o \ -+ rr_crypto.o hmac.o auth_opt.o mipv6_icmp.o module_cn.o -+ -+obj-m := $(O_TARGET) -+ -+mip6_ha-objs := halist.o mipv6_icmp_ha.o tunnel_ha.o \ -+ ndisc_ha.o ha.o module_ha.o -+ -+mip6_mn-objs := mipv6_icmp_mn.o ioctl_mn.o tunnel_mn.o \ -+ mdetect.o bul.o multiaccess_ctl.o mobhdr_mn.o mn.o \ -+ module_mn.o -+ -+obj-$(CONFIG_IPV6_MOBILITY_HA) += mip6_ha.o -+obj-$(CONFIG_IPV6_MOBILITY_MN) += mip6_mn.o -+ -+include $(TOPDIR)/Rules.make -+ -+mip6_ha.o: $(mip6_ha-objs) -+ $(LD) -r -o $@ $(mip6_ha-objs) -+ -+mip6_mn.o: $(mip6_mn-objs) -+ $(LD) -r -o $@ $(mip6_mn-objs) -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/README linux-2.4.25/net/ipv6/mobile_ip6/README ---- linux-2.4.25.old/net/ipv6/mobile_ip6/README 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/README 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,15 @@ -+MIPL Mobile IPv6 for Linux -+ -+More information at http://www.mipl.mediapoli.com/. -+ -+To join MIPL Mobile IPv6 for Linux mailing lists go to: -+ -+ http://www.mipl.mediapoli.com/cgi-bin/mailman/listinfo -+ -+Or send mail with subject "subscribe" for the general list to: -+ -+ mipl-request@list.mipl.mediapoli.com -+ -+or for the developer list to: -+ -+ mipl-devel-request@list.mail.mediapoli.com -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/auth_opt.c linux-2.4.25/net/ipv6/mobile_ip6/auth_opt.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/auth_opt.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/auth_opt.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,121 @@ -+/* -+ * MIPv6 Binding Authentication Data Option functions -+ * -+ * Authors: -+ * Henrik Petander <lpetande@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/icmpv6.h> -+#include <net/mipv6.h> -+ -+#include "debug.h" -+#include "hmac.h" -+#include "mobhdr.h" -+ -+#define DBG_KEY 5 -+ -+int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *mh, __u8 *aud_data, __u8 *k_bu) -+{ -+ /* First look up the peer from sadb based on his address */ -+ struct ah_processing ahp; -+ -+ /* Don't add any other options or this system is screwed */ -+ -+ __u8 buf[MAX_HASH_LENGTH]; -+ -+ -+ if (!k_bu) { -+ DEBUG(DBG_ERROR, "k_bu missing, aborting"); -+ return -1; -+ } -+ DEBUG(DBG_KEY, "Key for building authenticator:"); -+ debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE); -+ -+ if (ah_hmac_sha1_init(&ahp, k_bu, HMAC_SHA1_KEY_SIZE) < 0) { -+ DEBUG(DBG_ERROR, "Failed to initialize hmac sha1"); -+ return -1; -+ } -+ -+ DEBUG(DBG_KEY, "coa: "); -+ debug_print_buffer(DBG_KEY, coa, 16); -+ DEBUG(DBG_KEY, "cn_addr: "); -+ debug_print_buffer(DBG_KEY, cn_addr, 16); -+ DEBUG(DBG_KEY, "MH contents: "); -+ debug_print_buffer(DBG_KEY, mh, aud_data - mh); -+ -+ /* First the common part */ -+ ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr)); -+ ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr)); -+ ah_hmac_sha1_loop(&ahp, mh, aud_data - mh); -+ ah_hmac_sha1_result(&ahp, buf); -+ -+ memcpy(aud_data, buf, MIPV6_RR_MAC_LENGTH); -+ -+ return 0; -+} -+ -+int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *opt, __u8 optlen, -+ struct mipv6_mo_bauth_data *aud, __u8 *k_bu) -+{ -+ int ret = -1; -+ struct ah_processing ahp; -+ __u8 htarget[MAX_HASH_LENGTH]; -+ -+ /* Look up peer by home address */ -+ if (!k_bu) { -+ DEBUG(DBG_ERROR, "k_bu missing, aborting"); -+ return -1; -+ } -+ -+ DEBUG(DBG_KEY, "Key for checking authenticator:"); -+ debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE); -+ -+ if (!aud || !coa) { -+ DEBUG(DBG_INFO, "%s is NULL", aud ? "coa" : "aud"); -+ goto out; -+ } -+ -+ if (aud->length != MIPV6_RR_MAC_LENGTH) { -+ DEBUG(DBG_ERROR, -+ ": Incorrect authentication option length %d", aud->length); -+ goto out; -+ } -+ -+ if (ah_hmac_sha1_init(&ahp, k_bu, HMAC_SHA1_KEY_SIZE) < 0) { -+ DEBUG(DBG_ERROR, -+ "internal error in initialization of authentication algorithm"); -+ goto out; -+ } -+ DEBUG(DBG_KEY, "coa: "); -+ debug_print_buffer(DBG_KEY, coa, 16); -+ DEBUG(DBG_KEY, "cn_addr: "); -+ debug_print_buffer(DBG_KEY, cn_addr, 16); -+ DEBUG(DBG_KEY, "MH contents: "); -+ debug_print_buffer(DBG_KEY, opt, (u8*) aud->data - opt); -+ -+ ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr)); -+ ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr)); -+ -+ /* -+ * Process MH + options till the start of the authenticator in -+ * Auth. data option -+ */ -+ ah_hmac_sha1_loop(&ahp, opt, (u8 *)aud->data - opt); -+ ah_hmac_sha1_result(&ahp, htarget); -+ if (memcmp(htarget, aud->data, MIPV6_RR_MAC_LENGTH) == 0) -+ ret = 0; -+ -+ DEBUG(DBG_ERROR, "returning %d", ret); -+out: -+ return ret; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.c linux-2.4.25/net/ipv6/mobile_ip6/bcache.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bcache.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,746 @@ -+/* -+ * Binding Cache -+ * -+ * Authors: -+ * Juha Mynttinen <jmynttin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * Changes: -+ * -+ * Nanno Langstraat : Timer code cleaned up, active socket -+ * test rewritten -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/spinlock.h> -+#include <linux/proc_fs.h> -+#include <linux/ipv6_route.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/tcp.h> -+#include <net/udp.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "bcache.h" -+#include "hashlist.h" -+#include "debug.h" -+#include "mobhdr.h" -+#include "tunnel.h" -+#include "config.h" -+ -+#define TIMERDELAY HZ/10 -+ -+struct mipv6_bcache { -+ struct hashlist *entries; -+ __u32 size; -+ struct timer_list callback_timer; -+}; -+ -+struct in6_addr_pair { -+ struct in6_addr *a1; -+ struct in6_addr *a2; -+}; -+ -+static rwlock_t bcache_lock = RW_LOCK_UNLOCKED; -+ -+static struct mipv6_bcache bcache; -+ -+static int bcache_proc_info(char *buffer, char **start, off_t offset, -+ int length); -+ -+#define MIPV6_BCACHE_HASHSIZE 32 -+ -+/* Moment of transmission of a BR, in seconds before bcache entry expiry */ -+#define BCACHE_BR_SEND_LEAD 3 -+ -+#define MIPV6_MAX_BRR 3 /* Send 3 BRRs before deleting BC entry */ -+#define MIPV6_BRR_RATE HZ /* Send BRRs once per second */ -+ -+/* -+ * Internal functions. -+ */ -+ -+struct cache_entry_iterator_args { -+ struct mipv6_bce **entry; -+}; -+ -+static int find_first_cache_entry_iterator(void *data, void *args, -+ unsigned long *lifetime) -+{ -+ struct mipv6_bce *entry = -+ (struct mipv6_bce *) data; -+ struct cache_entry_iterator_args *state = -+ (struct cache_entry_iterator_args *) args; -+ -+ ASSERT(entry != NULL); -+ -+ if (entry->type == CACHE_ENTRY) { -+ *(state->entry) = entry; -+ return ITERATOR_STOP; /* stop iteration */ -+ } else { -+ return ITERATOR_CONT; /* continue iteration */ -+ } -+} -+ -+ -+/* -+ * Get memory for a new bcache entry. If bcache is full, a cache -+ * entry may be deleted to get space for a home registration, but not -+ * vice versa. -+ */ -+static struct mipv6_bce *mipv6_bce_alloc(__u8 type) -+{ -+ struct mipv6_bce *entry; -+ struct cache_entry_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ entry = (struct mipv6_bce *) -+ hashlist_alloc(bcache.entries, SLAB_ATOMIC); -+ -+ /* Cache replacement policy: always replace the CACHE_ENTRY -+ closest to expiration. Type HOME_REGISTRATION entry may -+ never be deleted before expiration. */ -+ if (entry == NULL) { -+ /* cache full, try to delete a CACHE_ENTRY */ -+ args.entry = &entry; -+ hashlist_iterate(bcache.entries, &args, -+ find_first_cache_entry_iterator); -+ if (entry == NULL) -+ return NULL; -+ hashlist_delete(bcache.entries, -+ (struct hashlist_entry *)entry); -+ entry = (struct mipv6_bce *) -+ hashlist_alloc(bcache.entries, SLAB_ATOMIC); -+ } -+ return entry; -+} -+ -+/* -+ * Frees entry's memory allocated with mipv6_bce_alloc -+ */ -+static void mipv6_bce_free(struct mipv6_bce *entry) -+{ -+ hashlist_free(bcache.entries, (void *) entry); -+} -+ -+/* -+ * Removes all expired entries -+ */ -+static void expire(void) -+{ -+ struct mipv6_bce *entry; -+ struct br_addrs { -+ struct in6_addr daddr; -+ struct in6_addr saddr; -+ struct br_addrs *next; -+ }; -+ struct br_addrs *br_info = NULL; -+ -+ DEBUG_FUNC(); -+ -+ write_lock(&bcache_lock); -+ -+ while ((entry = (struct mipv6_bce *) -+ hashlist_get_first(bcache.entries)) != NULL) { -+ struct rt6_info *rt; -+ if (time_after_eq(jiffies, entry->callback_time)) { -+ -+ DEBUG(DBG_INFO, "an entry expired"); -+ -+ if (entry->type & HOME_REGISTRATION) { -+ mip6_fn.proxy_del(&entry->home_addr, entry); -+ } -+ hashlist_delete(bcache.entries, (void *)entry); -+ mipv6_bce_free(entry); -+ entry = NULL; -+ } else if (entry->br_callback_time != 0 && -+ time_after_eq(jiffies, entry->br_callback_time) && -+ entry->br_count < MIPV6_MAX_BRR && -+ (rt = rt6_lookup(&entry->home_addr, &entry->our_addr, 0, 0)) != NULL){ -+ /* Do we have a destination cache entry for the home address */ -+ if (rt->rt6i_flags & RTF_CACHE) { -+ struct br_addrs *tmp; -+ tmp = br_info; -+ DEBUG(DBG_INFO, -+ "bcache entry recently used. Sending BR."); -+ /* queue for sending */ -+ br_info = kmalloc(sizeof(struct br_addrs), -+ GFP_ATOMIC); -+ if (br_info) { -+ ipv6_addr_copy(&br_info->saddr, -+ &entry->our_addr); -+ ipv6_addr_copy(&br_info->daddr, -+ &entry->home_addr); -+ br_info->next = tmp; -+ entry->last_br = jiffies; -+ entry->br_callback_time = jiffies + MIPV6_BRR_RATE; -+ entry->br_count++; -+ } else { -+ br_info = tmp; -+ DEBUG(DBG_ERROR, "Out of memory"); -+ } -+ -+ } else -+ entry->br_callback_time = 0; -+ dst_release(&rt->u.dst); -+ } else { -+ entry->br_callback_time = 0; -+ break; -+ } -+ } -+ write_unlock(&bcache_lock); -+ -+ while (br_info) { -+ struct br_addrs *tmp = br_info->next; -+ if (mipv6_send_brr(&br_info->saddr, &br_info->daddr, NULL) < 0) -+ DEBUG(DBG_WARNING, -+ "BR send for %x:%x:%x:%x:%x:%x:%x:%x failed", -+ NIPV6ADDR(&br_info->daddr)); -+ kfree(br_info); -+ br_info = tmp; -+ } -+} -+ -+static void set_timer(void) -+{ -+ struct mipv6_bce *entry; -+ unsigned long callback_time; -+ -+ DEBUG_FUNC(); -+ -+ entry = (struct mipv6_bce *) -+ hashlist_get_first(bcache.entries); -+ if (entry != NULL) { -+ if (entry->br_callback_time > 0 && -+ time_after(entry->br_callback_time, jiffies)) -+ callback_time = entry->br_callback_time; -+ else if (time_after(entry->callback_time, jiffies)) -+ callback_time = entry->callback_time; -+ else { -+ DEBUG(DBG_WARNING, -+ "bcache timer attempted to schedule" -+ " for a historical jiffies count!"); -+ callback_time = jiffies + TIMERDELAY; -+ } -+ -+ DEBUG(DBG_INFO, "setting timer to now"); -+ mod_timer(&bcache.callback_timer, callback_time); -+ } else { -+ del_timer(&bcache.callback_timer); -+ DEBUG(DBG_INFO, "BC empty, not setting a new timer"); -+ } -+} -+ -+/* -+ * The function that is scheduled to do the callback functions. May be -+ * modified e.g to allow Binding Requests, now only calls expire() and -+ * schedules a new timer. -+ */ -+static void timer_handler(unsigned long dummy) -+{ -+ expire(); -+ write_lock(&bcache_lock); -+ set_timer(); -+ write_unlock(&bcache_lock); -+} -+ -+/* -+ * Interface functions visible to other modules -+ */ -+ -+/** -+ * mipv6_bcache_add - add Binding Cache entry -+ * @ifindex: interface index -+ * @our_addr: own address -+ * @home_addr_org: MN's home address -+ * @coa: MN's care-of address -+ * @lifetime: lifetime for this binding -+ * @prefix: prefix length -+ * @seq: sequence number -+ * @flags: flags received in BU -+ * @type: type of entry -+ * -+ * Adds an entry for this @home_addr_org in the Binding Cache. If entry -+ * already exists, old entry is updated. @type may be %CACHE_ENTRY or -+ * %HOME_REGISTRATION. -+ **/ -+int mipv6_bcache_add(int ifindex, -+ struct in6_addr *our_addr, -+ struct in6_addr *home_addr, -+ struct in6_addr *coa, -+ __u32 lifetime, __u16 seq, __u8 flags, __u8 type) -+{ -+ struct mipv6_bce *entry; -+ int update = 0; -+ int create_tunnel = 0; -+ unsigned long now = jiffies; -+ struct in6_addr_pair hashkey; -+ int ret = -1; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ write_lock(&bcache_lock); -+ -+ if (type == HOME_REGISTRATION && !(mip6node_cnf.capabilities&CAP_HA)) -+ return 0; -+ -+ if (unlikely(bcache.entries == NULL)) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ if ((entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) != NULL) { -+ /* if an entry for this home_addr exists (with smaller -+ * seq than the new seq), update it by removing it -+ * first -+ */ -+ if (!MIPV6_SEQ_GT(seq, entry->seq)) { -+ DEBUG(DBG_INFO, "smaller seq than existing, not updating"); -+ goto out; -+ } -+ DEBUG(DBG_INFO, "updating an existing entry"); -+ update = 1; -+ -+ /* H-flag is already checked in BU handler. */ -+ /* XXX: Should we care about the other flags?*/ -+ if (flags != entry->flags) { -+ DEBUG(DBG_INFO, "entry/BU flag mismatch"); -+ } -+ -+ if (type == HOME_REGISTRATION) { -+ create_tunnel = (ipv6_addr_cmp(&entry->coa, coa) || -+ entry->ifindex != ifindex); -+ } -+ } else { -+ /* no entry for this home_addr, try to create a new entry */ -+ DEBUG(DBG_INFO, "creating a new entry"); -+ update = 0; -+ -+ entry = mipv6_bce_alloc(type); -+ if (entry == NULL) { -+ DEBUG(DBG_INFO, "cache full, entry not added"); -+ goto err; -+ } -+ -+ create_tunnel = (type == HOME_REGISTRATION); -+ } -+ -+ if (create_tunnel) { -+ if (update) -+ mip6_fn.proxy_del(&entry->home_addr, entry); -+ if (mip6_fn.proxy_create(flags, ifindex, coa, our_addr, home_addr) < 0) { -+ goto err_proxy; -+ } -+ } -+ -+ ipv6_addr_copy(&(entry->our_addr), our_addr); -+ ipv6_addr_copy(&(entry->home_addr), home_addr); -+ ipv6_addr_copy(&(entry->coa), coa); -+ entry->ifindex = ifindex; -+ entry->seq = seq; -+ entry->type = type; -+ entry->flags = flags; -+ -+ entry->last_br = 0; -+ entry->destunr_count = 0; -+ entry->callback_time = now + lifetime * HZ; -+ if (entry->type & HOME_REGISTRATION) -+ entry->br_callback_time = 0; -+ else -+ entry->br_callback_time = now + -+ (lifetime - BCACHE_BR_SEND_LEAD) * HZ; -+ -+ if (update) { -+ DEBUG(DBG_INFO, "updating entry : %x", entry); -+ hashlist_reposition(bcache.entries, (void *)entry, -+ entry->callback_time); -+ } else { -+ DEBUG(DBG_INFO, "adding entry: %x", entry); -+ if ((hashlist_add(bcache.entries, -+ &hashkey, -+ entry->callback_time, entry)) < 0) { -+ -+ DEBUG(DBG_ERROR, "Hash add failed"); -+ goto err_hashlist; -+ } -+ } -+ -+ set_timer(); -+ -+out: -+ write_unlock(&bcache_lock); -+ return 0; -+ -+err_hashlist: -+ if (create_tunnel) { -+ mip6_fn.proxy_del(home_addr, entry); -+ } -+err_proxy: -+ if (update) { -+ hashlist_delete(bcache.entries, (void *)entry); -+ } -+ mipv6_bce_free(entry); -+err: -+ write_unlock(&bcache_lock); -+ return ret; -+} -+ -+int mipv6_bcache_icmp_err(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ int destunr_count) -+{ -+ struct mipv6_bce *entry; -+ struct in6_addr_pair hashkey; -+ -+ int ret = -ENOENT; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ write_lock(&bcache_lock); -+ if (unlikely(bcache.entries == NULL)) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ if ((entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) != NULL) { -+ entry->last_destunr = jiffies; -+ entry->destunr_count = destunr_count; -+ ret = 0; -+ } -+err: -+ write_unlock(&bcache_lock); -+ return ret; -+} -+ -+ -+/** -+ * mipv6_bcache_delete - delete Binding Cache entry -+ * @home_addr: MN's home address -+ * @our_addr: our address -+ * @type: type of entry -+ * -+ * Deletes an entry associated with @home_addr from Binding Cache. -+ * Valid values for @type are %CACHE_ENTRY, %HOME_REGISTRATION and -+ * %ANY_ENTRY. %ANY_ENTRY deletes any type of entry. -+ **/ -+int mipv6_bcache_delete(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, __u8 type) -+{ -+ struct mipv6_bce *entry; -+ struct in6_addr_pair hashkey; -+ int err = 0; -+ -+ DEBUG_FUNC(); -+ -+ if (home_addr == NULL || our_addr == NULL) { -+ DEBUG(DBG_INFO, "error in arguments"); -+ return -EINVAL; -+ } -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ write_lock(&bcache_lock); -+ -+ if (unlikely(bcache.entries == NULL) || -+ (entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) == NULL || -+ !(entry->type & type)) { -+ DEBUG(DBG_INFO, "No matching entry found"); -+ err = -ENOENT; -+ goto out; -+ } -+ -+ hashlist_delete(bcache.entries, (void *) entry); -+ mipv6_bce_free(entry); -+ -+ set_timer(); -+out: -+ write_unlock(&bcache_lock); -+ return err; -+} -+ -+/** -+ * mipv6_bcache_exists - check if entry exists -+ * @home_addr: home address to check -+ * @our_addr: our address -+ * -+ * Determines if a binding exists for @home_addr. Returns type of the -+ * entry or negative if entry does not exist. -+ **/ -+int mipv6_bcache_exists(struct in6_addr *home_addr, -+ struct in6_addr *our_addr) -+{ -+ struct mipv6_bce *entry; -+ struct in6_addr_pair hashkey; -+ int type = -ENOENT; -+ -+ DEBUG_FUNC(); -+ -+ if (home_addr == NULL || our_addr == NULL) -+ return -EINVAL; -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ read_lock(&bcache_lock); -+ if (likely(bcache.entries != NULL) && -+ (entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) != NULL) { -+ type = entry->type; -+ } -+ read_unlock(&bcache_lock); -+ -+ return type; -+} -+ -+/** -+ * mipv6_bcache_get - get entry from Binding Cache -+ * @home_addr: home address to search -+ * @our_addr: our address -+ * @entry: pointer to buffer -+ * -+ * Gets a copy of Binding Cache entry for @home_addr. If entry -+ * exists entry is copied to @entry and zero is returned. -+ * Otherwise returns negative. -+ **/ -+int mipv6_bcache_get(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ struct mipv6_bce *entry) -+{ -+ struct mipv6_bce *entry2; -+ struct in6_addr_pair hashkey; -+ int ret = -ENOENT; -+ -+ DEBUG_FUNC(); -+ -+ if (home_addr == NULL || our_addr == NULL || entry == NULL) -+ return -EINVAL; -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ read_lock_bh(&bcache_lock); -+ -+ entry2 = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey); -+ if (entry2 != NULL) { -+ memcpy(entry, entry2, sizeof(struct mipv6_bce)); -+ ret = 0; -+ } -+ read_unlock_bh(&bcache_lock); -+ return ret; -+} -+ -+int mipv6_bcache_iterate(hashlist_iterator_t func, void *args) -+{ -+ int ret; -+ -+ read_lock_bh(&bcache_lock); -+ ret = hashlist_iterate(bcache.entries, args, func); -+ read_unlock_bh(&bcache_lock); -+ -+ return ret; -+} -+ -+/* -+ * Proc-filesystem functions -+ */ -+ -+#define BC_INFO_LEN 80 -+ -+struct procinfo_iterator_args { -+ char *buffer; -+ int offset; -+ int length; -+ int skip; -+ int len; -+}; -+ -+static int procinfo_iterator(void *data, void *args, unsigned long *pref) -+{ -+ struct procinfo_iterator_args *arg = -+ (struct procinfo_iterator_args *) args; -+ struct mipv6_bce *entry = -+ (struct mipv6_bce *) data; -+ -+ ASSERT(entry != NULL); -+ -+ if (arg->skip < arg->offset / BC_INFO_LEN) { -+ arg->skip++; -+ return ITERATOR_CONT; -+ } -+ -+ if (arg->len >= arg->length) -+ return ITERATOR_CONT; -+ -+ /* HoA CoA CallbackInSecs Type */ -+ arg->len += sprintf(arg->buffer + arg->len, -+ "%08x%08x%08x%08x %08x%08x%08x%08x %010lu %02d\n", -+ ntohl(entry->home_addr.s6_addr32[0]), -+ ntohl(entry->home_addr.s6_addr32[1]), -+ ntohl(entry->home_addr.s6_addr32[2]), -+ ntohl(entry->home_addr.s6_addr32[3]), -+ ntohl(entry->coa.s6_addr32[0]), -+ ntohl(entry->coa.s6_addr32[1]), -+ ntohl(entry->coa.s6_addr32[2]), -+ ntohl(entry->coa.s6_addr32[3]), -+ ((entry->callback_time) - jiffies) / HZ, -+ (int) entry->type); -+ -+ return ITERATOR_CONT; -+} -+ -+ /* -+ * Callback function for proc filesystem. -+ */ -+static int bcache_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct procinfo_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.buffer = buffer; -+ args.offset = offset; -+ args.length = length; -+ args.skip = 0; -+ args.len = 0; -+ -+ read_lock_bh(&bcache_lock); -+ hashlist_iterate(bcache.entries, &args, procinfo_iterator); -+ read_unlock_bh(&bcache_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % BC_INFO_LEN; -+ -+ args.len -= offset % BC_INFO_LEN; -+ -+ if (args.len > length) -+ args.len = length; -+ if (args.len < 0) -+ args.len = 0; -+ -+ return args.len; -+} -+ -+static int bcache_compare(void *data, void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey; -+ struct mipv6_bce *e = (struct mipv6_bce *) data; -+ -+ if (ipv6_addr_cmp(&e->home_addr, p->a1) == 0 -+ && ipv6_addr_cmp(&e->our_addr, p->a2) == 0) -+ return 0; -+ else -+ return -1; -+} -+ -+static __u32 bcache_hash(void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey; -+ -+ return p->a1->s6_addr32[0] ^ p->a1->s6_addr32[1] ^ -+ p->a2->s6_addr32[2] ^ p->a2->s6_addr32[3]; -+} -+ -+/* -+ * Initialization and shutdown functions -+ */ -+ -+int __init mipv6_bcache_init(__u32 size) -+{ -+ if (size < 1) { -+ DEBUG(DBG_ERROR, "Binding cache size must be at least 1"); -+ return -EINVAL; -+ } -+ bcache.entries = hashlist_create(MIPV6_BCACHE_HASHSIZE, size, -+ sizeof(struct mipv6_bce), -+ "mip6_bcache", NULL, NULL, -+ bcache_compare, bcache_hash); -+ -+ if (bcache.entries == NULL) { -+ DEBUG(DBG_ERROR, "Failed to initialize hashlist"); -+ return -ENOMEM; -+ } -+ -+ init_timer(&bcache.callback_timer); -+ bcache.callback_timer.data = 0; -+ bcache.callback_timer.function = timer_handler; -+ bcache.size = size; -+ -+ proc_net_create("mip6_bcache", 0, bcache_proc_info); -+ -+ DEBUG(DBG_INFO, "Binding cache initialized"); -+ return 0; -+} -+ -+static int -+bce_cleanup_iterator(void *rawentry, void *args, unsigned long *sortkey) -+{ -+ int type = (int) args; -+ struct mipv6_bce *entry = (struct mipv6_bce *) rawentry; -+ if (entry->type == type) { -+ if (entry->type & HOME_REGISTRATION) { -+ if (unlikely(mip6_fn.proxy_del == NULL)) -+ DEBUG(DBG_ERROR, "proxy_del unitialized"); -+ else -+ mip6_fn.proxy_del(&entry->home_addr, entry); -+ } -+ return ITERATOR_DELETE_ENTRY; -+ } -+ return ITERATOR_CONT; -+ -+} -+ -+void mipv6_bcache_cleanup(int type) -+{ -+ write_lock_bh(&bcache_lock); -+ hashlist_iterate(bcache.entries,(void *) type, bce_cleanup_iterator); -+ write_unlock_bh(&bcache_lock); -+} -+ -+int __exit mipv6_bcache_exit(void) -+{ -+ struct hashlist *entries; -+ -+ DEBUG_FUNC(); -+ -+ proc_net_remove("mip6_bcache"); -+ -+ write_lock_bh(&bcache_lock); -+ DEBUG(DBG_INFO, "Stopping the bcache timer"); -+ del_timer(&bcache.callback_timer); -+ hashlist_iterate(bcache.entries,(void *)CACHE_ENTRY, -+ bce_cleanup_iterator); -+ -+ entries = bcache.entries; -+ bcache.entries = NULL; -+ write_unlock_bh(&bcache_lock); -+ -+ hashlist_destroy(entries); -+ return 0; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.h linux-2.4.25/net/ipv6/mobile_ip6/bcache.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bcache.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bcache.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,72 @@ -+/* -+ * MIPL Mobile IPv6 Binding Cache header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _BCACHE_H -+#define _BCACHE_H -+ -+#include <linux/in6.h> -+#include <linux/timer.h> -+#include "hashlist.h" -+ -+#define CACHE_ENTRY 1 /* this and HOME_REGISTRATION are the entry types */ -+#define HOME_REGISTRATION 2 -+#define ANY_ENTRY 3 -+ -+#define MIPV6_MAX_DESTUNREACH 5 /* Delete CN BCEs after 5 destination unreachables */ -+#define MIPV6_DEST_UNR_IVAL 10 /* What is the max interval of destination -+ unreacahable error messages for them to be persistent*/ -+ -+struct mipv6_bce { -+ struct hashlist_entry e; -+ int ifindex; /* Interface identifier */ -+ struct in6_addr our_addr; /* our address (as seen by the MN) */ -+ struct in6_addr home_addr; /* MN home address */ -+ struct in6_addr coa; /* MN care-of address */ -+ unsigned long callback_time; /* time of expiration (in jiffies) */ -+ unsigned long br_callback_time; /* time for sending a BR (in jiffies) */ -+ int (*callback_function)(struct mipv6_bce *entry); -+ __u8 type; /* home registration */ -+ __u8 router; /* mn is router */ -+ __u8 flags; /* flags received in BU */ -+ __u16 seq; /* sequence number */ -+ unsigned long last_br; /* time when last BR sent */ -+ unsigned long last_destunr; /* time when last ICMP destination unreachable received */ -+ int br_count; /* How many BRRs have sent */ -+ int destunr_count; /* Number of destination unreachables received */ -+}; -+ -+int mipv6_bcache_add(int ifindex, struct in6_addr *our_addr, -+ struct in6_addr *home_addr, struct in6_addr *coa, -+ __u32 lifetime, __u16 seq, __u8 flags, __u8 type); -+ -+int mipv6_bcache_icmp_err(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ int destunr_count); -+ -+int mipv6_bcache_delete(struct in6_addr *home_addr, struct in6_addr *our_addr, -+ __u8 type); -+ -+int mipv6_bcache_exists(struct in6_addr *home_addr, -+ struct in6_addr *our_addr); -+ -+int mipv6_bcache_get(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ struct mipv6_bce *entry); -+ -+int mipv6_bcache_iterate(int (*func)(void *, void *, unsigned long *), void *args); -+ -+void mipv6_bcache_cleanup(int type); -+ -+int mipv6_bcache_init(__u32 size); -+ -+int mipv6_bcache_exit(void); -+ -+#endif /* _BCACHE_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bul.c linux-2.4.25/net/ipv6/mobile_ip6/bul.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bul.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bul.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,634 @@ -+/* -+ * Binding update list -+ * -+ * Authors: -+ * Juha Mynttinen <jmynttin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * Changes: -+ * -+ * Nanno Langstraat : Timer code cleaned up -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/spinlock.h> -+#include <net/ipv6.h> -+#include <net/mipv6.h> -+#include <linux/proc_fs.h> -+ -+#include "bul.h" -+#include "debug.h" -+#include "hashlist.h" -+#include "tunnel_mn.h" -+#include "mobhdr.h" -+ -+#define MIPV6_BUL_HASHSIZE 32 -+ -+rwlock_t bul_lock = RW_LOCK_UNLOCKED; -+ -+struct mipv6_bul { -+ struct hashlist *entries; -+ struct timer_list callback_timer; -+}; -+ -+static struct mipv6_bul bul; -+ -+struct in6_addr_pair { -+ struct in6_addr *a1; -+ struct in6_addr *a2; -+}; -+ -+/********************************************************************** -+ * -+ * Private functions -+ * -+ **********************************************************************/ -+ -+static int bul_compare(void *data, void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey; -+ struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data; -+ -+ if (ipv6_addr_cmp(&e->cn_addr, p->a1) == 0 -+ && ipv6_addr_cmp(&e->home_addr, p->a2) == 0) -+ return 0; -+ else -+ return -1; -+} -+ -+struct test_keys { -+ struct in6_addr *addr; -+ u8 *cookie; -+}; -+ -+static int bul_compare_cookie(void *data, void *keys) -+{ -+ struct test_keys *p = (struct test_keys *)keys; -+ struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data; -+ -+ if (ipv6_addr_cmp(&e->cn_addr, p->addr) == 0 && e->rr -+ && memcmp(&e->rr->cot_cookie, p->cookie, 8) == 0) -+ return 0; -+ else -+ return -1; -+} -+ -+static u32 bul_hash(void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey; -+ -+ return p->a1->s6_addr32[0] ^ -+ p->a1->s6_addr32[1] ^ -+ p->a1->s6_addr32[2] ^ -+ p->a1->s6_addr32[3]; -+} -+ -+static int bul_proc_info(char *buffer, char **start, off_t offset, -+ int length); -+ -+static struct mipv6_bul_entry *mipv6_bul_get_entry(void) -+{ -+ DEBUG_FUNC(); -+ return ((struct mipv6_bul_entry *) -+ hashlist_alloc(bul.entries, SLAB_ATOMIC)); -+} -+ -+static void mipv6_bul_entry_free(struct mipv6_bul_entry *entry) -+{ -+ DEBUG_FUNC(); -+ -+ if (entry->rr) { -+ if (entry->rr->kbu) -+ kfree(entry->rr->kbu); -+ kfree(entry->rr); -+ } -+ if (entry->ops) -+ kfree(entry->ops); -+ hashlist_free(bul.entries, (void *)entry); -+} -+ -+static __inline__ int del_bul_entry_tnl(struct mipv6_bul_entry *entry) -+{ -+ if (entry->flags & MIPV6_BU_F_HOME) { -+ return mipv6_mv_tnl_to_ha(&entry->cn_addr, -+ &entry->coa, -+ &entry->home_addr); -+ } -+ return 0; -+} -+ -+static void timer_update(void) -+{ -+ struct mipv6_bul_entry *entry; -+ -+ DEBUG_FUNC(); -+ -+ entry = hashlist_get_first(bul.entries); -+ -+ while (entry && time_after_eq(jiffies, entry->callback_time)) { -+ if (time_after_eq(jiffies, entry->expire) || -+ entry->callback(entry) != 0) { -+ /* -+ * Either the entry has expired, or the callback -+ * indicated that it should be deleted. -+ */ -+ hashlist_delete(bul.entries, (void *)entry); -+ -+ del_bul_entry_tnl(entry); -+ mipv6_bul_entry_free(entry); -+ DEBUG(DBG_INFO, "Entry deleted (was expired) from " -+ "binding update list"); -+ } else { -+ /* move entry to its right place in the hashlist */ -+ DEBUG(DBG_INFO, "Rescheduling"); -+ hashlist_reposition(bul.entries, (void *)entry, -+ entry->callback_time); -+ } -+ entry = (struct mipv6_bul_entry *) -+ hashlist_get_first(bul.entries); -+ } -+ -+ if (entry == NULL) { -+ DEBUG(DBG_INFO, "bul empty, not setting a new timer"); -+ del_timer(&bul.callback_timer); -+ } else { -+ mod_timer(&bul.callback_timer, entry->callback_time); -+ } -+} -+ -+static void timer_handler(unsigned long dummy) -+{ -+ DEBUG_FUNC(); -+ -+ write_lock(&bul_lock); -+ timer_update(); -+ write_unlock(&bul_lock); -+} -+ -+/********************************************************************** -+ * -+ * Public interface functions -+ * -+ **********************************************************************/ -+ -+/** -+ * mipv6_bul_iterate - apply interator function to all entries -+ * @func: function to apply -+ * @args: extra arguments for iterator -+ * -+ * Applies @func for each entry in Binding Update List. Extra -+ * arguments given in @args are also passed to the iterator function. -+ * Caller must hold @bul_lock. -+ **/ -+int mipv6_bul_iterate(hashlist_iterator_t func, void *args) -+{ -+ DEBUG_FUNC(); -+ -+ return hashlist_iterate(bul.entries, args, func); -+} -+ -+/** -+ * mipv6_bul_exists - check if Binding Update List entry exists -+ * @cn: address to check -+ * -+ * Checks if Binding Update List has an entry for @cn. Returns true -+ * if entry exists, false otherwise. Caller may not hold @bul_lock. -+ **/ -+int mipv6_bul_exists(struct in6_addr *cn, struct in6_addr *haddr) -+{ -+ int exists; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = cn; -+ hashkey.a2 = haddr; -+ -+ read_lock_bh(&bul_lock); -+ -+ if (unlikely(bul.entries == NULL)) -+ exists = 0; -+ else -+ exists = (hashlist_get(bul.entries, &hashkey) != NULL); -+ -+ read_unlock_bh(&bul_lock); -+ return exists; -+} -+ -+/** -+ * mipv6_bul_get - get Binding Update List entry -+ * @cn_addr: CN address to search -+ * @home_addr: home address to search -+ * -+ * Returns Binding Update List entry for @cn_addr if it exists. -+ * Otherwise returns %NULL. Caller must hold @bul_lock. -+ **/ -+struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cn_addr, -+ struct in6_addr *home_addr) -+{ -+ struct mipv6_bul_entry *entry; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ if (unlikely(bul.entries == NULL)) { -+ return NULL; -+ } -+ hashkey.a1 = cn_addr; -+ hashkey.a2 = home_addr; -+ -+ entry = (struct mipv6_bul_entry *) -+ hashlist_get(bul.entries, &hashkey); -+ -+ return entry; -+} -+ -+struct mipv6_bul_entry *mipv6_bul_get_by_ccookie( -+ struct in6_addr *cn_addr, u8 *cookie) -+{ -+ struct test_keys key; -+ -+ DEBUG_FUNC(); -+ -+ if (unlikely(bul.entries == NULL)) -+ return NULL; -+ key.addr = cn_addr; -+ key.cookie = cookie; -+ -+ return (struct mipv6_bul_entry *) -+ hashlist_get_ex(bul.entries, &key, -+ bul_compare_cookie); -+} -+ -+/** -+ * mipv6_bul_reschedule - reschedule Binding Update List entry -+ * @entry: entry to reschedule -+ * -+ * Reschedules a Binding Update List entry. Must be called after -+ * modifying entry lifetime. Caller must hold @bul_lock (write). -+ **/ -+void mipv6_bul_reschedule(struct mipv6_bul_entry *entry) -+{ -+ DEBUG_FUNC(); -+ -+ hashlist_reposition(bul.entries, -+ (void *)entry, -+ entry->callback_time); -+ timer_update(); -+} -+ -+/** -+ * mipv6_bul_add - add binding update to Binding Update List -+ * @cn_addr: IPv6 address where BU was sent -+ * @home_addr: Home address for this binding -+ * @coa: Care-of address for this binding -+ * @lifetime: expiration time of the binding in seconds -+ * @seq: sequence number of the BU -+ * @flags: %MIPV6_BU_F_* flags -+ * @callback: callback function called on expiration -+ * @callback_time: expiration time for callback -+ * @state: binding send state -+ * @delay: retransmission delay -+ * @maxdelay: retransmission maximum delay -+ * @ops: Mobility header options for BU -+ * @rr: Return routability information -+ * -+ * Adds a binding update sent to @cn_addr for @home_addr to the -+ * Binding Update List. If entry already exists, it is updated. -+ * Entry is set to expire in @lifetime seconds. Entry has a callback -+ * function @callback that is called at @callback_time. Entry @state -+ * controls resending of this binding update and it can be set to -+ * %ACK_OK, %RESEND_EXP or %ACK_ERROR. Returns a pointer to the newly -+ * created or updated entry. Caller must hold @bul_lock (write). -+ **/ -+struct mipv6_bul_entry *mipv6_bul_add( -+ struct in6_addr *cn_addr, struct in6_addr *home_addr, -+ struct in6_addr *coa, -+ __u32 lifetime, __u16 seq, __u8 flags, -+ int (*callback)(struct mipv6_bul_entry *entry), -+ __u32 callback_time, -+ __u8 state, __u32 delay, __u32 maxdelay, -+ struct mipv6_mh_opt *ops, -+ struct mipv6_rr_info *rr) -+{ -+ struct mipv6_bul_entry *entry; -+ int update = 0; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ if (unlikely(bul.entries == NULL)) -+ return NULL; -+ -+ if (cn_addr == NULL || home_addr == NULL || coa == NULL || -+ lifetime < 0 || callback == NULL || callback_time < 0 || -+ (state != ACK_OK && state != RESEND_EXP && state != ACK_ERROR) || -+ delay < 0 || maxdelay < 0) { -+ DEBUG(DBG_ERROR, "invalid arguments"); -+ return NULL; -+ } -+ DEBUG(DBG_INFO, "cn_addr: %x:%x:%x:%x:%x:%x:%x:%x, " -+ "home_addr: %x:%x:%x:%x:%x:%x:%x:%x" -+ "coaddr: %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(cn_addr), -+ NIPV6ADDR(home_addr), NIPV6ADDR(coa)); -+ hashkey.a1 = cn_addr; -+ hashkey.a2 = home_addr; -+ -+ /* -+ * decide whether to add a new entry or update existing, also -+ * check if there's room for a new entry when adding a new -+ * entry (latter is handled by mipv6_bul_get_entry() -+ */ -+ if ((entry = (struct mipv6_bul_entry *) -+ hashlist_get(bul.entries, &hashkey)) != NULL) { -+ /* if an entry for this cn_addr exists (with smaller -+ * seq than the new entry's seq), update it */ -+ -+ if (MIPV6_SEQ_GT(seq, entry->seq)) { -+ DEBUG(DBG_INFO, "updating an existing entry"); -+ update = 1; -+ } else { -+ DEBUG(DBG_INFO, "smaller seq than existing, not updating"); -+ return NULL; -+ } -+ } else { -+ entry = mipv6_bul_get_entry(); -+ if (entry == NULL) { -+ DEBUG(DBG_WARNING, "binding update list full, can't add!!!"); -+ return NULL; -+ } -+ memset(entry, 0, sizeof(*entry)); -+ /* First BU send happens here, save count in the entry */ -+ entry->consecutive_sends = 1; -+ } -+ -+ if (!update) { -+ ipv6_addr_copy(&(entry->cn_addr), cn_addr); -+ ipv6_addr_copy(&(entry->home_addr), home_addr); -+ entry->ops = ops; -+ } -+ /* Add Return Routability info to bul entry */ -+ if (rr) { -+ if(entry->rr) -+ kfree(entry->rr); -+ entry->rr = rr; -+ } -+ -+ ipv6_addr_copy(&(entry->coa), coa); -+ entry->lifetime = lifetime; -+ if (lifetime) -+ entry->expire = jiffies + lifetime * HZ; -+ else if (flags & MIPV6_BU_F_ACK) -+ entry->expire = jiffies + HOME_RESEND_EXPIRE * HZ; -+ entry->seq = seq; -+ entry->flags = flags; -+ entry->lastsend = jiffies; /* current time = last use of the entry */ -+ entry->state = state; -+ entry->delay = delay; -+ entry->maxdelay = maxdelay; -+ entry->callback_time = jiffies + callback_time * HZ; -+ entry->callback = callback; -+ -+ if (flags & MIPV6_BU_F_HOME && -+ mipv6_mv_tnl_to_ha(cn_addr, coa, home_addr)) { -+ DEBUG(DBG_ERROR, "reconfiguration of the tunnel failed"); -+ } -+ if (update) { -+ DEBUG(DBG_INFO, "updating entry: %x", entry); -+ hashlist_reposition(bul.entries, (void *)entry, -+ entry->callback_time); -+ } else { -+ DEBUG(DBG_INFO, "adding entry: %x", entry); -+ -+ hashkey.a1 = &entry->cn_addr; -+ hashkey.a2 = &entry->home_addr; -+ -+ if ((hashlist_add(bul.entries, &hashkey, -+ entry->callback_time, -+ entry)) < 0) { -+ DEBUG(DBG_ERROR, "Hash add failed"); -+ mipv6_bul_entry_free(entry); -+ return NULL; -+ } -+ } -+ timer_update(); -+ -+ return entry; -+} -+ -+/** -+ * mipv6_bul_delete - delete Binding Update List entry -+ * @cn_addr: address for entry to delete -+ * -+ * Deletes the entry for @cn_addr from the Binding Update List. -+ * Returns zero if entry was deleted succesfully, otherwise returns -+ * negative. Caller may not hold @bul_lock. -+ **/ -+int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr) -+{ -+ struct mipv6_bul_entry *entry; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = cn_addr; -+ hashkey.a2 = home_addr; -+ -+ write_lock(&bul_lock); -+ -+ if (unlikely(bul.entries == NULL) || -+ (entry = (struct mipv6_bul_entry *) -+ hashlist_get(bul.entries, &hashkey)) == NULL) { -+ write_unlock(&bul_lock); -+ DEBUG(DBG_INFO, "No such entry"); -+ return -ENOENT; -+ } -+ -+ hashlist_delete(bul.entries, (void *)entry); -+ -+ del_bul_entry_tnl(entry); -+ -+ mipv6_bul_entry_free(entry); -+ timer_update(); -+ write_unlock(&bul_lock); -+ -+ DEBUG(DBG_INFO, "Binding update list entry deleted"); -+ -+ return 0; -+} -+ -+/********************************************************************** -+ * -+ * Proc interface functions -+ * -+ **********************************************************************/ -+ -+#define BUL_INFO_LEN 152 -+ -+struct procinfo_iterator_args { -+ char *buffer; -+ int offset; -+ int length; -+ int skip; -+ int len; -+}; -+ -+static int procinfo_iterator(void *data, void *args, -+ unsigned long *sortkey) -+{ -+ struct procinfo_iterator_args *arg = -+ (struct procinfo_iterator_args *)args; -+ struct mipv6_bul_entry *entry = -+ (struct mipv6_bul_entry *)data; -+ unsigned long callback_seconds; -+ -+ DEBUG_FUNC(); -+ -+ if (entry == NULL) return ITERATOR_ERR; -+ -+ if (time_after(jiffies, entry->callback_time)) -+ callback_seconds = 0; -+ else -+ callback_seconds = (entry->callback_time - jiffies) / HZ; -+ -+ if (arg->skip < arg->offset / BUL_INFO_LEN) { -+ arg->skip++; -+ return ITERATOR_CONT; -+ } -+ -+ if (arg->len >= arg->length) -+ return ITERATOR_CONT; -+ -+ /* CN HoA CoA ExpInSecs SeqNum State Delay MaxDelay CallbackInSecs */ -+ arg->len += sprintf(arg->buffer + arg->len, -+ "%08x%08x%08x%08x %08x%08x%08x%08x %08x%08x%08x%08x\n" -+ "%010lu %05d %02d %010d %010d %010lu\n", -+ ntohl(entry->cn_addr.s6_addr32[0]), -+ ntohl(entry->cn_addr.s6_addr32[1]), -+ ntohl(entry->cn_addr.s6_addr32[2]), -+ ntohl(entry->cn_addr.s6_addr32[3]), -+ ntohl(entry->home_addr.s6_addr32[0]), -+ ntohl(entry->home_addr.s6_addr32[1]), -+ ntohl(entry->home_addr.s6_addr32[2]), -+ ntohl(entry->home_addr.s6_addr32[3]), -+ ntohl(entry->coa.s6_addr32[0]), -+ ntohl(entry->coa.s6_addr32[1]), -+ ntohl(entry->coa.s6_addr32[2]), -+ ntohl(entry->coa.s6_addr32[3]), -+ (entry->expire - jiffies) / HZ, -+ entry->seq, entry->state, entry->delay, -+ entry->maxdelay, callback_seconds); -+ -+ return ITERATOR_CONT; -+} -+ -+ -+/* -+ * Callback function for proc filesystem. -+ */ -+static int bul_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct procinfo_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.buffer = buffer; -+ args.offset = offset; -+ args.length = length; -+ args.skip = 0; -+ args.len = 0; -+ -+ read_lock_bh(&bul_lock); -+ hashlist_iterate(bul.entries, &args, procinfo_iterator); -+ read_unlock_bh(&bul_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % BUL_INFO_LEN; -+ -+ args.len -= offset % BUL_INFO_LEN; -+ -+ if (args.len > length) -+ args.len = length; -+ if (args.len < 0) -+ args.len = 0; -+ -+ return args.len; -+} -+ -+/********************************************************************** -+ * -+ * Code module init/fini functions -+ * -+ **********************************************************************/ -+ -+int __init mipv6_bul_init(__u32 size) -+{ -+ DEBUG_FUNC(); -+ -+ if (size < 1) { -+ DEBUG(DBG_CRITICAL, -+ "Binding update list size must be at least 1"); -+ return -EINVAL; -+ } -+ bul.entries = hashlist_create(MIPV6_BUL_HASHSIZE, size, -+ sizeof(struct mipv6_bul_entry), -+ "mip6_bul", NULL, NULL, -+ bul_compare, bul_hash); -+ -+ if (bul.entries == NULL) { -+ DEBUG(DBG_CRITICAL, "Couldn't allocate memory for " -+ "hashlist when creating a binding update list"); -+ return -ENOMEM; -+ } -+ init_timer(&bul.callback_timer); -+ bul.callback_timer.data = 0; -+ bul.callback_timer.function = timer_handler; -+ proc_net_create("mip6_bul", 0, bul_proc_info); -+ DEBUG(DBG_INFO, "Binding update list initialized"); -+ return 0; -+} -+ -+void __exit mipv6_bul_exit() -+{ -+ struct mipv6_bul_entry *entry; -+ struct hashlist *entries; -+ -+ DEBUG_FUNC(); -+ -+ proc_net_remove("mip6_bul"); -+ -+ write_lock_bh(&bul_lock); -+ -+ DEBUG(DBG_INFO, "Stopping the bul timer"); -+ del_timer(&bul.callback_timer); -+ -+ while ((entry = (struct mipv6_bul_entry *) -+ hashlist_get_first(bul.entries)) != NULL) { -+ hashlist_delete(bul.entries, (void *)entry); -+ -+ del_bul_entry_tnl(entry); -+ -+ mipv6_bul_entry_free(entry); -+ } -+ entries = bul.entries; -+ bul.entries = NULL; -+ write_unlock_bh(&bul_lock); -+ -+ hashlist_destroy(entries); -+ -+ DEBUG(DBG_INFO, "binding update list destroyed"); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/bul.h linux-2.4.25/net/ipv6/mobile_ip6/bul.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/bul.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/bul.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,91 @@ -+/* -+ * MIPL Mobile IPv6 Binding Update List header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _BUL_H -+#define _BUL_H -+ -+#include "hashlist.h" -+ -+#define ACK_OK 0x01 -+#define RESEND_EXP 0x02 -+#define ACK_ERROR 0x04 -+ -+#define HOME_RESEND_EXPIRE 3600 -+#define MIPV6_COOKIE_LEN 8 -+struct mipv6_rr_info { -+ /* RR information */ -+ u16 rr_state; /* State of the RR */ -+ u16 rr_flags; /* Flags for the RR */ -+ u8 hot_cookie[MIPV6_COOKIE_LEN]; /* HoT Cookie */ -+ u8 cot_cookie[MIPV6_COOKIE_LEN]; /* CoT Cookie */ -+ u8 home_cookie[MIPV6_COOKIE_LEN]; /* Home Cookie */ -+ u8 careof_cookie[MIPV6_COOKIE_LEN]; /* Careof Cookie */ -+ u32 lastsend_hoti; /* When HoTI was last sent (jiffies) */ -+ u32 lastsend_coti; /* When CoTI was last sent (jiffies) */ -+ u32 home_time; /* when Care-of cookie was received */ -+ u32 careof_time; /* when Home cookie was received */ -+ int home_nonce_index; /* Home cookie nonce index */ -+ int careof_nonce_index; /* Care-of cookie nonce index */ -+ u8 *kbu; /* Binding authentication key */ -+}; -+struct mipv6_bul_entry { -+ struct hashlist_entry e; -+ struct in6_addr cn_addr; /* CN to which BU was sent */ -+ struct in6_addr home_addr; /* home address of this binding */ -+ struct in6_addr coa; /* care-of address of the sent BU */ -+ -+ unsigned long expire; /* entry's expiration time (jiffies) */ -+ __u32 lifetime; /* lifetime sent in this BU */ -+ __u32 lastsend; /* last time when BU sent (jiffies) */ -+ __u32 consecutive_sends; /* Number of consecutive BU's sent */ -+ __u16 seq; /* sequence number of the latest BU */ -+ __u8 flags; /* BU send flags */ -+ __u8 state; /* resend state */ -+ __u32 initdelay; /* initial ack wait */ -+ __u32 delay; /* current ack wait */ -+ __u32 maxdelay; /* maximum ack wait */ -+ -+ struct mipv6_rr_info *rr; -+ struct mipv6_mh_opt *ops; /* saved option values */ -+ -+ unsigned long callback_time; -+ int (*callback)(struct mipv6_bul_entry *entry); -+}; -+ -+extern rwlock_t bul_lock; -+ -+int mipv6_bul_init(__u32 size); -+ -+void mipv6_bul_exit(void); -+ -+struct mipv6_bul_entry *mipv6_bul_add( -+ struct in6_addr *cn_addr, struct in6_addr *home_addr, -+ struct in6_addr *coa, __u32 lifetime, __u16 seq, __u8 flags, -+ int (*callback)(struct mipv6_bul_entry *entry), __u32 callback_time, -+ __u8 state, __u32 delay, __u32 maxdelay, struct mipv6_mh_opt *ops, -+ struct mipv6_rr_info *rr); -+ -+int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr); -+ -+int mipv6_bul_exists(struct in6_addr *cnaddr, struct in6_addr *home_addr); -+ -+struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cnaddr, -+ struct in6_addr *home_addr); -+struct mipv6_bul_entry *mipv6_bul_get_by_ccookie(struct in6_addr *cn_addr, -+ u8 *cookie); -+ -+int bul_entry_expired(struct mipv6_bul_entry *bulentry); -+ -+void mipv6_bul_reschedule(struct mipv6_bul_entry *entry); -+ -+int mipv6_bul_iterate(int (*func)(void *, void *, unsigned long *), void *args); -+ -+#endif /* BUL_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/config.h linux-2.4.25/net/ipv6/mobile_ip6/config.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/config.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/config.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,72 @@ -+/* -+ * Configuration parameters -+ * -+ * $Id$ -+ */ -+ -+#define MIPV6VERSION "D24" -+#define MIPLVERSION "v1.0" -+ -+#define CAP_CN 0x01 -+#define CAP_HA 0x02 -+#define CAP_MN 0x04 -+ -+struct mip6_conf { -+ int capabilities; -+ int debug_level; -+ int accept_ret_rout; -+ int max_rtr_reachable_time; -+ int eager_cell_switching; -+ int max_num_tunnels; -+ int min_num_tunnels; -+ int binding_refresh_advice; -+ int bu_lladdr; -+ int bu_keymgm; -+ int bu_cn_ack; -+}; -+ -+extern struct mip6_conf mip6node_cnf; -+ -+struct mipv6_bce; -+ -+struct mip6_func { -+ void (*bce_home_add) (int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ void (*bce_cache_add) (int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ void (*bce_home_del) (struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu); -+ void (*bce_cache_del) (struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu); -+ -+ int (*bce_tnl_rt_add) (struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+ void (*bce_tnl_rt_del) (struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+ void (*proxy_del) (struct in6_addr *home_addr, struct mipv6_bce *entry); -+ int (*proxy_create) (int flags, int ifindex, struct in6_addr *coa, -+ struct in6_addr *our_addr, struct in6_addr *home_addr); -+ -+ int (*icmpv6_dhaad_rep_rcv) (struct sk_buff *skb); -+ int (*icmpv6_dhaad_req_rcv) (struct sk_buff *skb); -+ int (*icmpv6_pfxadv_rcv) (struct sk_buff *skb); -+ int (*icmpv6_pfxsol_rcv) (struct sk_buff *skb); -+ int (*icmpv6_paramprob_rcv) (struct sk_buff *skb); -+ -+ int (*mn_use_hao) (struct in6_addr *daddr, struct in6_addr *saddr); -+ void (*mn_check_tunneled_packet) (struct sk_buff *skb); -+}; -+ -+extern struct mip6_func mip6_fn; -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/debug.h linux-2.4.25/net/ipv6/mobile_ip6/debug.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/debug.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/debug.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,112 @@ -+/* -+ * MIPL Mobile IPv6 Debugging macros and functions -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _DEBUG_H -+#define _DEBUG_H -+ -+#include <linux/autoconf.h> -+ -+/* priorities for different debug conditions */ -+ -+#define DBG_CRITICAL 0 /* unrecoverable error */ -+#define DBG_ERROR 1 /* error (recoverable) */ -+#define DBG_WARNING 2 /* unusual situation but not a real error */ -+#define DBG_INFO 3 /* generally useful information */ -+#define DBG_EXTRA 4 /* extra information */ -+#define DBG_FUNC_ENTRY 6 /* use to indicate function entry and exit */ -+#define DBG_DATADUMP 7 /* packet dumps, etc. lots of flood */ -+ -+/** -+ * NIPV6ADDR - macro for IPv6 addresses -+ * @addr: Network byte order IPv6 address -+ * -+ * Macro for printing IPv6 addresses. Used in conjunction with -+ * printk() or derivatives (such as DEBUG macro). -+ **/ -+#define NIPV6ADDR(addr) \ -+ ntohs(((u16 *)addr)[0]), \ -+ ntohs(((u16 *)addr)[1]), \ -+ ntohs(((u16 *)addr)[2]), \ -+ ntohs(((u16 *)addr)[3]), \ -+ ntohs(((u16 *)addr)[4]), \ -+ ntohs(((u16 *)addr)[5]), \ -+ ntohs(((u16 *)addr)[6]), \ -+ ntohs(((u16 *)addr)[7]) -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+extern int mipv6_debug; -+ -+/** -+ * debug_print - print debug message -+ * @debug_level: message priority -+ * @fname: calling function's name -+ * @fmt: printf-style formatting string -+ * -+ * Prints a debug message to system log if @debug_level is less or -+ * equal to @mipv6_debug. Should always be called using DEBUG() -+ * macro, not directly. -+ **/ -+static void debug_print(int debug_level, const char *fname, const char* fmt, ...) -+{ -+ char s[1024]; -+ va_list args; -+ -+ if (mipv6_debug < debug_level) -+ return; -+ -+ va_start(args, fmt); -+ vsprintf(s, fmt, args); -+ printk("mip6[%s]: %s\n", fname, s); -+ va_end(args); -+} -+ -+/** -+ * debug_print_buffer - print arbitrary buffer to system log -+ * @debug_level: message priority -+ * @data: pointer to buffer -+ * @len: number of bytes to print -+ * -+ * Prints @len bytes from buffer @data to system log. @debug_level -+ * tells on which debug level message gets printed. For -+ * debug_print_buffer() priority %DBG_DATADUMP should be used. -+ **/ -+#define debug_print_buffer(debug_level,data,len) { \ -+ if (mipv6_debug >= debug_level) { \ -+ int i; \ -+ for (i=0; i<len; i++) { \ -+ if (i%16 == 0) printk("\n%04x: ", i); \ -+ printk("%02x ", ((unsigned char *)data)[i]); \ -+ } \ -+ printk("\n\n"); \ -+ } \ -+} -+ -+#define DEBUG(x,y,z...) debug_print(x,__FUNCTION__,y,##z) -+#define DEBUG_FUNC() \ -+DEBUG(DBG_FUNC_ENTRY, "%s(%d)/%s: ", __FILE__,__LINE__,__FUNCTION__) -+ -+#else -+#define DEBUG(x,y,z...) -+#define DEBUG_FUNC() -+#define debug_print_buffer(x,y,z) -+#endif -+ -+#undef ASSERT -+#define ASSERT(expression) { \ -+ if (!(expression)) { \ -+ (void)printk(KERN_ERR \ -+ "Assertion \"%s\" failed: file \"%s\", function \"%s\", line %d\n", \ -+ #expression, __FILE__, __FUNCTION__, __LINE__); \ -+ BUG(); \ -+ } \ -+} -+ -+#endif /* _DEBUG_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.c linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,394 @@ -+/* -+ * Extension Header handling and adding code -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/types.h> -+#include <linux/slab.h> -+ -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "debug.h" -+#include "stats.h" -+#include "mobhdr.h" -+#include "bcache.h" -+#include "config.h" -+ -+/** -+ * mipv6_append_home_addr - Add Home Address Option -+ * @opt: buffer for Home Address Option -+ * @offset: offset from beginning of @opt -+ * @addr: address for HAO -+ * -+ * Adds a Home Address Option to a packet. Option is stored in -+ * @offset from beginning of @opt. The option is created but the -+ * original source address in IPv6 header is left intact. The source -+ * address will be changed from home address to CoA after the checksum -+ * has been calculated in getfrag. Padding is done automatically, and -+ * @opt must have allocated space for both actual option and pad. -+ * Returns offset from @opt to end of options. -+ **/ -+int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr) -+{ -+ int pad; -+ struct mipv6_dstopt_homeaddr *ho; -+ -+ DEBUG(DBG_DATADUMP, "HAO: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(addr)); -+ -+ pad = (6 - offset) & 7; -+ mipv6_add_pad(opt + offset, pad); -+ -+ ho = (struct mipv6_dstopt_homeaddr *)(opt + offset + pad); -+ ho->type = MIPV6_TLV_HOMEADDR; -+ ho->length = sizeof(*ho) - 2; -+ ipv6_addr_copy(&ho->addr, addr); -+ -+ return offset + pad + sizeof(*ho); -+} -+static inline int check_hao_validity(struct mipv6_dstopt_homeaddr *haopt, -+ u8 *dst1, -+ struct in6_addr *saddr, -+ struct in6_addr *daddr) -+{ -+ int addr_type = ipv6_addr_type(&haopt->addr); -+ struct mipv6_bce bc_entry; -+ -+ if (addr_type & IPV6_ADDR_LINKLOCAL || -+ !(addr_type & IPV6_ADDR_UNICAST)) { -+ DEBUG(DBG_INFO, "HAO with link local or non-unicast HoA, " -+ "not sending BE to " -+ "home address " -+ "%x:%x:%x:%x:%x:%x:%x:%x ", -+ "care-of address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&haopt->addr), -+ NIPV6ADDR(saddr)); -+ return -EINVAL; -+ } else if (dst1[0] != IPPROTO_MOBILITY && -+ (mipv6_bcache_get(&haopt->addr, -+ daddr, &bc_entry) != 0 || -+ ipv6_addr_cmp(saddr, &bc_entry.coa))) { -+ DEBUG(DBG_INFO, "HAO without binding or incorrect CoA, " -+ "sending BE code 1: " -+ "home address %x:%x:%x:%x:%x:%x:%x:%x", -+ "to care-of address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&haopt->addr), -+ NIPV6ADDR(saddr)); -+ return -ENOENT; -+ } -+ return 0; -+} -+/** -+ * mipv6_handle_homeaddr - Home Address Destination Option handler -+ * @skb: packet buffer -+ * @optoff: offset to where option begins -+ * -+ * Handles Home Address Option in IPv6 Destination Option header. -+ * Packet and offset to option are passed. If HAO is used without -+ * binding, sends a Binding Error code 1. When sending BE, notify bit -+ * is cleared to prevent IPv6 error handling from sending ICMP -+ * Parameter Problem. Returns 1 on success, otherwise zero. -+ **/ -+int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff) -+{ -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr coaddr; -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; -+ struct mipv6_dstopt_homeaddr *haopt = -+ (struct mipv6_dstopt_homeaddr *) &skb->nh.raw[optoff]; -+ u8 *dst1; -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ if (haopt->length != sizeof(*haopt) - 2) { -+ DEBUG(DBG_WARNING, "HAO has invalid length"); -+ MIPV6_INC_STATS(n_ha_drop.invalid); -+ return 0; -+ } -+ dst1 = (u8 *)skb->h.raw; -+ err = check_hao_validity(haopt, dst1, saddr, &skb->nh.ipv6h->daddr); -+ -+ if (err) { -+ haopt->type &= ~(0x80); /* clear notify bit */ -+ if (err == -ENOENT) -+ mipv6_send_be(&skb->nh.ipv6h->daddr, saddr, -+ &haopt->addr, MIPV6_BE_HAO_WO_BINDING); -+ MIPV6_INC_STATS(n_ha_drop.misc); -+ return 0; -+ } -+ ipv6_addr_copy(&coaddr, saddr); -+ ipv6_addr_copy(saddr, &haopt->addr); -+ ipv6_addr_copy(&haopt->addr, &coaddr); -+ opt->hao = optoff; -+ if (mip6_fn.mn_check_tunneled_packet != NULL) -+ mip6_fn.mn_check_tunneled_packet(skb); -+ -+ MIPV6_INC_STATS(n_ha_rcvd); -+ return 1; -+} -+ -+/** -+ * mipv6_icmp_swap_addrs - Switch HAO and src and RT2 and dest for ICMP errors -+ * @skb: packet buffer -+ * -+ * Reset the source address and the Home Address option in skb before -+ * appending it to an ICMP error message, so original packet appears -+ * in the error message rather than mangled. -+ **/ -+void mipv6_icmp_swap_addrs(struct sk_buff *skb) -+{ -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; -+ struct in6_addr tmp; -+ struct in6_addr *hoa; -+ DEBUG_FUNC(); -+ if (opt->srcrt2) { -+ struct rt2_hdr *rt2; -+ rt2 = (struct rt2_hdr *)(skb->nh.raw + opt->srcrt2); -+ hoa = &rt2->addr; -+ -+ ipv6_addr_copy(&tmp, hoa); -+ ipv6_addr_copy(hoa, &skb->nh.ipv6h->daddr); -+ ipv6_addr_copy(&skb->nh.ipv6h->daddr, &tmp); -+ rt2->rt_hdr.segments_left++; -+ skb->nh.ipv6h->hop_limit++; -+ } -+ if (opt->hao) { -+ struct mipv6_dstopt_homeaddr *hao; -+ hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao); -+ hoa = &hao->addr; -+ -+ ipv6_addr_copy(&tmp, hoa); -+ ipv6_addr_copy(hoa, &skb->nh.ipv6h->saddr); -+ ipv6_addr_copy(&skb->nh.ipv6h->saddr, &tmp); -+ } -+} -+ -+/** -+ * mipv6_append_rt2hdr - Add Type 2 Routing Header -+ * @rt: buffer for new routing header -+ * @addr: intermediate hop address -+ * -+ * Adds a Routing Header Type 2 in a packet. Stores newly created -+ * routing header in buffer @rt. Type 2 RT only carries one address, -+ * so there is no need to process old routing header. @rt must have -+ * allocated space for 24 bytes. -+ **/ -+void mipv6_append_rt2hdr(struct ipv6_rt_hdr *rt, struct in6_addr *addr) -+{ -+ struct rt2_hdr *rt2 = (struct rt2_hdr *)rt; -+ -+ DEBUG(DBG_DATADUMP, "RT2: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(addr)); -+ -+ if (ipv6_addr_type(addr) == IPV6_ADDR_MULTICAST) { -+ DEBUG(DBG_ERROR, "destination address not unicast"); -+ return; -+ } -+ -+ memset(rt2, 0, sizeof(*rt2)); -+ rt2->rt_hdr.type = 2; -+ rt2->rt_hdr.hdrlen = 2; -+ rt2->rt_hdr.segments_left = 1; -+ ipv6_addr_copy(&rt2->addr, addr); -+} -+ -+/** -+ * mipv6_append_dst1opts - Add Destination Option (1) Headers -+ * @dst1opt: buffer for new destination options -+ * @saddr: address for Home Address Option -+ * @old_dst1opt: old destination options -+ * @len: length of options -+ * -+ * Adds Destination Option (1) Header to a packet. New options are -+ * stored in @dst1opt. If old destination options exist, they are -+ * copied from @old_dst1opt. Only Home Address Option is destination -+ * option. @dstopt must have allocated space for @len bytes. @len -+ * includes Destination Option Header (2 bytes), Home Address Option -+ * (18 bytes) and possible HAO pad (8n+6). -+ **/ -+/* -+ * ISSUE: Home Address Destination Option should really be added to a -+ * new destination option header specified in Mobile IPv6 spec which -+ * should be placed after routing header(s), but before fragmentation -+ * header. Putting HAO in DO1 works for now, but support for the new -+ * placement should be added to the IPv6 stack. -+ */ -+void -+mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr, -+ struct ipv6_opt_hdr *old_dst1opt, int len) -+{ -+ int offset; -+ -+ if (old_dst1opt) { -+ memcpy(dst1opt, old_dst1opt, ipv6_optlen(old_dst1opt)); -+ offset = ipv6_optlen(old_dst1opt); -+ } else { -+ offset = sizeof (*dst1opt); -+ } -+ dst1opt->hdrlen = (len >> 3) - 1; -+ mipv6_append_home_addr((__u8 *) dst1opt, offset, saddr); -+} -+ -+/** -+ * mipv6_modify_txoptions - Modify outgoing packets -+ * @sk: socket -+ * @skb: packet buffer for outgoing packet -+ * @old_opt: transmit options -+ * @fl: packet flow structure -+ * @dst: pointer to destination cache entry -+ * -+ * Adds Home Address Option (for MN packets, when not at home) and -+ * Routing Header Type 2 (for CN packets when sending to an MN) to -+ * data packets. Old extension headers are copied from @old_opt (if -+ * any). Extension headers are _explicitly_ added for packets with -+ * Mobility Header. Returns the new header structure, or old if no -+ * changes. -+ **/ -+struct ipv6_txoptions * -+mipv6_modify_txoptions(struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *old_opt, struct flowi *fl, -+ struct dst_entry **dst) -+{ -+ struct ipv6_opt_hdr *old_hopopt = NULL; -+ struct ipv6_opt_hdr *old_dst1opt = NULL; -+ struct ipv6_rt_hdr *old_srcrt = NULL; -+ -+ int srcrtlen = 0, dst1len = 0; -+ int tot_len, use_hao = 0; -+ struct ipv6_txoptions *opt; -+ struct mipv6_bce bc_entry; -+ struct in6_addr tmpaddr, *saddr, *daddr, coaddr; -+ __u8 *opt_ptr; -+ -+ DEBUG_FUNC(); -+ -+ if (fl->proto == IPPROTO_MOBILITY) return old_opt; -+ /* -+ * we have to be prepared to the fact that saddr might not be present, -+ * if that is the case, we acquire saddr just as kernel does. -+ */ -+ saddr = fl ? fl->fl6_src : NULL; -+ daddr = fl ? fl->fl6_dst : NULL; -+ -+ if (daddr == NULL) -+ return old_opt; -+ if (saddr == NULL) { -+ int err = ipv6_get_saddr(NULL, daddr, &tmpaddr); -+ if (err) -+ return old_opt; -+ else -+ saddr = &tmpaddr; -+ } -+ -+ DEBUG(DBG_DATADUMP, -+ "dest. address of packet: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(daddr)); -+ DEBUG(DBG_DATADUMP, " and src. address: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(saddr)); -+ -+ if (old_opt) { -+ old_hopopt = old_opt->hopopt; -+ old_dst1opt = old_opt->dst1opt; -+ old_srcrt = old_opt->srcrt; -+ } -+ -+ if (mip6_fn.mn_use_hao != NULL) -+ use_hao = mip6_fn.mn_use_hao(daddr, saddr); -+ -+ if (use_hao) { -+ if (old_dst1opt) -+ dst1len = ipv6_optlen(old_dst1opt); -+ dst1len += sizeof(struct mipv6_dstopt_homeaddr) + -+ ((6 - dst1len) & 7); /* padding */ -+ } -+ -+ if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0) -+ srcrtlen = sizeof(struct rt2_hdr); -+ -+ if ((tot_len = srcrtlen + dst1len) == 0) { -+ return old_opt; -+ } -+ -+ tot_len += sizeof(*opt); -+ -+ if (!(opt = kmalloc(tot_len, GFP_ATOMIC))) { -+ return NULL; -+ } -+ memset(opt, 0, tot_len); -+ opt->tot_len = tot_len; -+ opt_ptr = (__u8 *) (opt + 1); -+ -+ if (old_srcrt) { -+ opt->srcrt = old_srcrt; -+ opt->opt_nflen += ipv6_optlen(old_srcrt); -+ } -+ -+ if (srcrtlen) { -+ DEBUG(DBG_DATADUMP, "Binding exists. Adding routing header"); -+ -+ opt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr; -+ opt->opt_nflen += srcrtlen; -+ opt_ptr += srcrtlen; -+ -+ /* -+ * Append care-of-address to routing header (original -+ * destination address is home address, the first -+ * source route segment gets put to the destination -+ * address and the home address gets to the last -+ * segment of source route (just as it should)) -+ */ -+ -+ ipv6_addr_copy(&coaddr, &bc_entry.coa); -+ -+ mipv6_append_rt2hdr(opt->srcrt2, &coaddr); -+ -+ /* -+ * reroute output (we have to do this in case of TCP -+ * segment) unless a routing header of type 0 is also added -+ */ -+ if (dst && !opt->srcrt) { -+ struct in6_addr *tmp = fl->fl6_dst; -+ fl->fl6_dst = &coaddr; -+ -+ dst_release(*dst); -+ *dst = ip6_route_output(sk, fl); -+ if (skb) -+ skb->dst = *dst; -+ fl->fl6_dst = tmp; -+ -+ DEBUG(DBG_DATADUMP, "Rerouted outgoing packet"); -+ } -+ } -+ -+ /* Only home address option is inserted to first dst opt header */ -+ if (dst1len) { -+ opt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr; -+ opt->opt_flen += dst1len; -+ opt_ptr += dst1len; -+ mipv6_append_dst1opts(opt->dst1opt, saddr, -+ old_dst1opt, dst1len); -+ opt->mipv6_flags = MIPV6_SND_HAO; -+ } else if (old_dst1opt) { -+ opt->dst1opt = old_dst1opt; -+ opt->opt_flen += ipv6_optlen(old_dst1opt); -+ } -+ if (old_hopopt) { -+ opt->hopopt = old_hopopt; -+ opt->opt_nflen += ipv6_optlen(old_hopopt); -+ } -+ -+ return opt; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.h linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/exthdrs.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/exthdrs.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,47 @@ -+/* -+ * MIPL Mobile IPv6 Extension Headers header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MIPV6_EXTHDRS_H -+#define _MIPV6_EXTHDRS_H -+ -+struct in6_addr; -+struct sk_buff; -+struct ipv6_rt_hdr; -+struct ipv6_opt_hdr; -+struct ipv6_txoptions; -+struct flowi; -+struct dst_entry; -+/* -+ * Home Address Destination Option function prototypes -+ */ -+int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr); -+ -+int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff); -+ -+void mipv6_icmp_swap_addrs(struct sk_buff *skb); -+ -+/* -+ * Creates a routing header of type 2. -+ */ -+void mipv6_append_rt2hdr(struct ipv6_rt_hdr *srcrt, struct in6_addr *addr); -+ -+/* Function to add the first destination option header, which may -+ * include a home address option. -+ */ -+void mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr, -+ struct ipv6_opt_hdr *old_dst1opt, int len); -+ -+struct ipv6_txoptions *mipv6_modify_txoptions( -+ struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *old_opt, struct flowi *fl, -+ struct dst_entry **dst); -+ -+#endif /* _MIPV6_EXTHDRS_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ha.c linux-2.4.25/net/ipv6/mobile_ip6/ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ha.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,553 @@ -+/* -+ * Home-agent functionality -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Henrik Petander <lpetande@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Changes: Venkata Jagana, -+ * Krishna Kumar : Statistics fix -+ * Masahide Nakamura : Use of mipv6_forward -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/net.h> -+#include <linux/skbuff.h> -+#include <linux/if_ether.h> -+#include <linux/netdevice.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/netfilter.h> -+#include <linux/netfilter_ipv6.h> -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif -+ -+#include <net/neighbour.h> -+#include <net/ipv6.h> -+#include <net/ip6_fib.h> -+#include <net/ip6_route.h> -+#include <net/ndisc.h> -+#include <net/addrconf.h> -+#include <net/neighbour.h> -+ -+#include "tunnel_ha.h" -+#include "bcache.h" -+#include "stats.h" -+#include "debug.h" -+#include "util.h" -+#include "ha.h" -+#include "config.h" -+#include "mobhdr.h" -+ -+static int mipv6_ha_tunnel_sitelocal = 0; -+ -+#ifdef CONFIG_SYSCTL -+ -+static struct ctl_table_header *mipv6_ha_sysctl_header; -+ -+static struct mipv6_ha_sysctl_table -+{ -+ struct ctl_table_header *sysctl_header; -+ ctl_table mipv6_vars[3]; -+ ctl_table mipv6_mobility_table[2]; -+ ctl_table mipv6_proto_table[2]; -+ ctl_table mipv6_root_table[2]; -+} mipv6_ha_sysctl = { -+ NULL, -+ -+ {{NET_IPV6_MOBILITY_TUNNEL_SITELOCAL, "tunnel_sitelocal", -+ &mipv6_ha_tunnel_sitelocal, sizeof(int), 0644, NULL, -+ &proc_dointvec}, -+ {0}}, -+ -+ {{NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, -+ mipv6_ha_sysctl.mipv6_vars}, {0}}, -+ {{NET_IPV6, "ipv6", NULL, 0, 0555, -+ mipv6_ha_sysctl.mipv6_mobility_table}, {0}}, -+ {{CTL_NET, "net", NULL, 0, 0555, -+ mipv6_ha_sysctl.mipv6_proto_table}, {0}} -+}; -+ -+#endif /* CONFIG_SYSCTL */ -+ -+ -+/* this is defined in kernel IPv6 module (sockglue.c) */ -+extern struct packet_type ipv6_packet_type; -+ -+/* mipv6_forward: Intercept NS packets destined to home address of MN */ -+int mipv6_forward(struct sk_buff *skb) -+{ -+ struct ipv6hdr *ipv6h; -+ struct in6_addr *daddr, *saddr; -+ __u8 nexthdr; -+ int nhoff; -+ -+ if (skb == NULL) return 0; -+ -+ ipv6h = skb->nh.ipv6h; -+ daddr = &ipv6h->daddr; -+ saddr = &ipv6h->saddr; -+ -+ nexthdr = ipv6h->nexthdr; -+ nhoff = sizeof(*ipv6h); -+ -+ if (ipv6_ext_hdr(nexthdr)) -+ nhoff = ipv6_skip_exthdr(skb, nhoff, &nexthdr, -+ skb->len - sizeof(*ipv6h)); -+ -+ /* Do not to forward Neighbor Solicitation to Home Address of MN */ -+ if (nexthdr == IPPROTO_ICMPV6) { -+ struct icmp6hdr *icmp6h; -+ int dest_type; -+ -+ if (nhoff < 0 || !pskb_may_pull(skb, nhoff + -+ sizeof(struct icmp6hdr))) { -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ dest_type = ipv6_addr_type(daddr); -+ icmp6h = (struct icmp6hdr *)&skb->nh.raw[nhoff]; -+ -+ /* Intercepts NS to HoA of MN */ -+ -+ if ((icmp6h->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) || -+ ((dest_type & IPV6_ADDR_MULTICAST) && -+ (icmp6h->icmp6_type == NDISC_ROUTER_ADVERTISEMENT))) { -+ ip6_input(skb); -+ } else { -+ ip6_forward(skb); -+ } -+ } else { -+ ip6_forward(skb); -+ } -+ return 0; -+} -+ -+ -+/** -+ * mipv6_proxy_nd_rem - stop acting as a proxy for @home_address -+ * @home_addr: address to remove -+ * @ha_addr: home agent's address on home link -+ * @linklocal: link-local compatibility bit -+ * -+ * When Home Agent acts as a proxy for an address it must leave the -+ * solicited node multicast group for that address and stop responding -+ * to neighbour solicitations. -+ **/ -+static int mipv6_proxy_nd_rem(struct in6_addr *home_addr, -+ int ifindex, int linklocal) -+{ -+ /* When MN returns home HA leaves the solicited mcast groups -+ * for MNs home addresses -+ */ -+ int err; -+ struct net_device *dev; -+ -+ DEBUG_FUNC(); -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) { -+ DEBUG(DBG_ERROR, "couldn't get dev"); -+ return -ENODEV; -+ } -+#if 1 /* TEST */ -+ /* Remove link-local entry */ -+ if (linklocal) { -+ struct in6_addr ll_addr; -+ mipv6_generate_ll_addr(&ll_addr, home_addr); -+ if ((err = pneigh_delete(&nd_tbl, &ll_addr, dev)) < 0) { -+ DEBUG(DBG_INFO, -+ "peigh_delete failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&ll_addr)); -+ } -+ } -+#endif -+ /* Remove global (or site-local) entry */ -+ if ((err = pneigh_delete(&nd_tbl, home_addr, dev)) < 0) { -+ DEBUG(DBG_INFO, -+ "peigh_delete failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(home_addr)); -+ } -+ dev_put(dev); -+ return err; -+} -+ -+/** -+ * mipv6_proxy_nd - join multicast group for this address -+ * @home_addr: address to defend -+ * @ha_addr: home agent's address on home link -+ * @linklocal: link-local compatibility bit -+ * -+ * While Mobile Node is away from home, Home Agent acts as a proxy for -+ * @home_address. HA responds to neighbour solicitations for @home_address -+ * thus getting all packets destined to home address of MN. -+ **/ -+static int mipv6_proxy_nd(struct in6_addr *home_addr, -+ int ifindex, int linklocal) -+{ -+ /* The HA sends a proxy ndisc_na message to all hosts on MN's -+ * home subnet by sending a neighbor advertisement with the -+ * home address or all addresses of the mobile node if the -+ * prefix is not 0. The addresses are formed by combining the -+ * suffix or the host part of the address with each subnet -+ * prefix that exists in the home subnet -+ */ -+ -+ /* Since no previous entry for MN exists a proxy_nd advertisement -+ * is sent to all nodes link local multicast address -+ */ -+ int err = -1; -+ -+ struct net_device *dev; -+ struct in6_addr na_saddr; -+ struct in6_addr ll_addr; -+ struct pneigh_entry *ll_pneigh; -+ struct in6_addr mcdest; -+ int send_ll_na = 0; -+ int inc_opt = 1; -+ int solicited = 0; -+ int override = 1; -+ -+ DEBUG_FUNC(); -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) { -+ DEBUG(DBG_ERROR, "couldn't get dev"); -+ return -ENODEV; -+ } -+ -+ if (!pneigh_lookup(&nd_tbl, home_addr, dev, 1)) { -+ DEBUG(DBG_INFO, -+ "peigh_lookup failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(home_addr)); -+ goto free_dev; -+ } -+#if 1 /* TEST */ -+ if (linklocal) { -+ mipv6_generate_ll_addr(&ll_addr, home_addr); -+ -+ if ((ll_pneigh = pneigh_lookup(&nd_tbl, &ll_addr, -+ dev, 1)) == NULL) { -+ DEBUG(DBG_INFO, -+ "peigh_lookup failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&ll_addr)); -+ pneigh_delete(&nd_tbl, home_addr, dev); -+ goto free_dev; -+ } else { -+ send_ll_na = 1; -+ } -+ } else { -+ ll_pneigh = NULL; -+ } -+#endif -+ /* Proxy neighbor advertisement of MN's home address -+ * to all nodes solicited multicast address -+ */ -+ if (!ipv6_get_lladdr(dev, &na_saddr)) { -+ ipv6_addr_all_nodes(&mcdest); -+ ndisc_send_na(dev, NULL, &mcdest, home_addr, 0, -+ solicited, override, inc_opt); -+#if 1 /* TEST */ -+ if (send_ll_na) { -+ ndisc_send_na(dev, NULL, &mcdest, &ll_addr, -+ 0, solicited, override, inc_opt); -+ } -+#endif -+ err = 0; -+ } else { -+ DEBUG(DBG_ERROR, "failed to get link local address for sending proxy NA"); -+ } -+free_dev: -+ dev_put(dev); -+ return err; -+ -+} -+ -+struct inet6_ifaddr *is_on_link_ipv6_address(struct in6_addr *mn_haddr, -+ struct in6_addr *ha_addr) -+{ -+ struct inet6_ifaddr *ifp; -+ struct inet6_dev *in6_dev; -+ struct inet6_ifaddr *oifp = NULL; -+ -+ if ((ifp = ipv6_get_ifaddr(ha_addr, 0)) == NULL) -+ return NULL; -+ -+ if ((in6_dev = ifp->idev) != NULL) { -+ in6_dev_hold(in6_dev); -+ oifp = in6_dev->addr_list; -+ while (oifp != NULL) { -+ spin_lock(&oifp->lock); -+ if (mipv6_prefix_compare(&oifp->addr, mn_haddr, -+ oifp->prefix_len) && -+ !(oifp->flags & IFA_F_TENTATIVE)) { -+ spin_unlock(&oifp->lock); -+ DEBUG(DBG_INFO, "Home Addr Opt: on-link"); -+ in6_ifa_hold(oifp); -+ break; -+ } -+ spin_unlock(&oifp->lock); -+ oifp = oifp->if_next; -+ } -+ in6_dev_put(in6_dev); -+ } -+ in6_ifa_put(ifp); -+/* DEBUG(DBG_WARNING, "Home Addr Opt NOT on-link"); */ -+ return oifp; -+ -+} -+ -+/* -+ * Lifetime checks. ifp->valid_lft >= ifp->prefered_lft always (see addrconf.c) -+ * Returned value is in seconds. -+ */ -+ -+static __u32 get_min_lifetime(struct inet6_ifaddr *ifp, __u32 lifetime) -+{ -+ __u32 rem_lifetime = 0; -+ unsigned long now = jiffies; -+ -+ if (ifp->valid_lft == 0) { -+ rem_lifetime = lifetime; -+ } else { -+ __u32 valid_lft_left = -+ ifp->valid_lft - ((now - ifp->tstamp) / HZ); -+ rem_lifetime = -+ min_t(unsigned long, valid_lft_left, lifetime); -+ } -+ -+ return rem_lifetime; -+} -+ -+#define MAX_LIFETIME 1000 -+ -+/** -+ * mipv6_lifetime_check - check maximum lifetime is not exceeded -+ * @lifetime: lifetime to check -+ * -+ * Checks @lifetime does not exceed %MAX_LIFETIME. Returns @lifetime -+ * if not exceeded, otherwise returns %MAX_LIFETIME. -+ **/ -+static int mipv6_lifetime_check(int lifetime) -+{ -+ return (lifetime > MAX_LIFETIME) ? MAX_LIFETIME : lifetime; -+} -+ -+/* Generic routine handling finish of BU processing */ -+void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, __u8 ba_status, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 ba_lifetime, __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ int err; -+ -+ if (ba_status >= REASON_UNSPECIFIED) { -+ /* DAD failed */ -+ goto out; -+ } -+ -+ ba_lifetime = get_min_lifetime(ifp, ba_lifetime); -+ ba_lifetime = mipv6_lifetime_check(ba_lifetime); -+ -+ if ((err = mipv6_bcache_add(ifindex, daddr, haddr, coa, -+ ba_lifetime, sequence, flags, -+ HOME_REGISTRATION)) != 0 ) { -+ DEBUG(DBG_WARNING, "home reg failed."); -+ -+ if (err == -ENOMEDIUM) -+ return; -+ -+ ba_status = INSUFFICIENT_RESOURCES; -+ } else { -+ DEBUG(DBG_INFO, "home reg succeeded."); -+ } -+ -+ DEBUG(DBG_DATADUMP, "home_addr: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(haddr)); -+ DEBUG(DBG_DATADUMP, "coa: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(coa)); -+ DEBUG(DBG_DATADUMP, "lifet:%d, seq:%d", ba_lifetime, sequence); -+out: -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence, -+ ba_lifetime, k_bu); -+} -+ -+static int ha_proxy_create(int flags, int ifindex, struct in6_addr *coa, -+ struct in6_addr *our_addr, struct in6_addr *home_addr) -+{ -+ int ret; -+ -+ if ((ret = mipv6_add_tnl_to_mn(coa, our_addr, home_addr)) <= 0) { -+ if (ret != -ENOMEDIUM) { -+ DEBUG(DBG_ERROR, "unable to configure tunnel to MN!"); -+ } -+ return -1; -+ } -+ if (mipv6_proxy_nd(home_addr, ifindex, -+ flags & MIPV6_BU_F_LLADDR) != 0) { -+ DEBUG(DBG_ERROR, "mipv6_proxy_nd failed!"); -+ mipv6_del_tnl_to_mn(coa, our_addr, home_addr); -+ return -2; -+ } -+ return 0; -+} -+ -+static void ha_proxy_del(struct in6_addr *home_addr, struct mipv6_bce *entry) -+{ -+ if (mipv6_proxy_nd_rem(&entry->home_addr, entry->ifindex, -+ entry->flags & MIPV6_BU_F_LLADDR) == 0) { -+ DEBUG(DBG_INFO, "proxy_nd succ"); -+ } else { -+ DEBUG(DBG_INFO, "proxy_nd fail"); -+ } -+ mipv6_del_tnl_to_mn(&entry->coa, &entry->our_addr, home_addr); -+} -+ -+static void bc_home_add(int ifindex, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 lifetime, __u16 sequence, __u8 flags, -+ __u8 *k_bu) -+{ -+ struct inet6_ifaddr *ifp = NULL; -+ __u8 ba_status = SUCCESS; -+ -+ DEBUG_FUNC(); -+ -+ ifp = is_on_link_ipv6_address(haddr, daddr); -+ -+ if (ifp == NULL) { -+ ba_status = NOT_HOME_SUBNET; -+ } else if (((ipv6_addr_type(haddr) & IPV6_ADDR_SITELOCAL) || -+ (ipv6_addr_type(coa) & IPV6_ADDR_SITELOCAL)) -+ && !mipv6_ha_tunnel_sitelocal) { -+ /* Site-local home or care-of addresses are not -+ accepted by default */ -+ ba_status = ADMINISTRATIVELY_PROHIBITED; -+ } else { -+ int ret; -+ -+ ifindex = ifp->idev->dev->ifindex; -+ -+ if ((ret = mipv6_dad_start(ifp, ifindex, daddr, -+ haddr, coa, rep_coa, lifetime, -+ sequence, flags)) < 0) { -+ /* An error occurred */ -+ ba_status = -ret; -+ } else if (ret) { -+ /* DAD is needed to be performed. */ -+ in6_ifa_put(ifp); -+ return; -+ } -+ } -+ -+ mipv6_bu_finish(ifp, ifindex, ba_status, daddr, haddr, coa, -+ rep_coa, lifetime, sequence, flags, k_bu); -+ if (ifp) -+ in6_ifa_put(ifp); -+} -+ -+static void bc_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ __u8 status = SUCCESS; -+ struct mipv6_bce bce; -+ -+ /* Primary Care-of Address Deregistration */ -+ if (mipv6_bcache_get(haddr, daddr, &bce) < 0) { -+ DEBUG(DBG_INFO, "entry is not in cache"); -+ status = NOT_HA_FOR_MN; -+ } else { -+ ha_proxy_del(&bce.home_addr, &bce); -+ mipv6_bcache_delete(haddr, daddr, HOME_REGISTRATION); -+ } -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, 0, k_bu); -+} -+ -+extern int mipv6_ra_rcv_ptr(struct sk_buff *skb, struct icmp6hdr *msg); -+ -+ -+static int -+mipv6_ha_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_encapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_ha_tnl_xmit_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_ENCAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_ha_tnl_xmit_stats_hook -+}; -+ -+static int -+mipv6_ha_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_decapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_ha_tnl_rcv_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_DECAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_ha_tnl_rcv_stats_hook -+}; -+ -+static struct mip6_func old; -+ -+int __init mipv6_ha_init(void) -+{ -+ DEBUG_FUNC(); -+ -+#ifdef CONFIG_SYSCTL -+ if (!(mipv6_ha_sysctl_header = -+ register_sysctl_table(mipv6_ha_sysctl.mipv6_root_table, 0))) -+ printk(KERN_ERR "Failed to register sysctl handlers!"); -+#endif -+ memcpy(&old, &mip6_fn, sizeof(struct mip6_func)); -+ mip6_fn.bce_home_add = bc_home_add; -+ mip6_fn.bce_home_del = bc_home_delete; -+ mip6_fn.proxy_del = ha_proxy_del; -+ mip6_fn.proxy_create = ha_proxy_create; -+ /* register packet interception hooks */ -+ ip6ip6_tnl_register_hook(&mipv6_ha_tnl_xmit_stats_ops); -+ ip6ip6_tnl_register_hook(&mipv6_ha_tnl_rcv_stats_ops); -+ return 0; -+} -+ -+void __exit mipv6_ha_exit(void) -+{ -+ DEBUG_FUNC(); -+ -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_ha_sysctl_header); -+#endif -+ -+ /* remove packet interception hooks */ -+ ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_rcv_stats_ops); -+ ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_xmit_stats_ops); -+ -+ mip6_fn.bce_home_add = old.bce_home_add; -+ mip6_fn.bce_home_del = old.bce_home_del; -+ mip6_fn.proxy_del = old.proxy_del; -+ mip6_fn.proxy_create = old.proxy_create; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ha.h linux-2.4.25/net/ipv6/mobile_ip6/ha.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ha.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ha.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,39 @@ -+/* -+ * MIPL Mobile IPv6 Home Agent header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HA_H -+#define _HA_H -+ -+int mipv6_ha_init(void); -+void mipv6_ha_exit(void); -+ -+int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 ba_lifetime, __u16 sequence, __u8 flags); -+ -+void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, -+ __u8 ba_status, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 ba_lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ -+ -+static __inline__ void mipv6_generate_ll_addr(struct in6_addr *ll_addr, -+ struct in6_addr *addr) -+{ -+ ll_addr->s6_addr32[0] = htonl(0xfe800000); -+ ll_addr->s6_addr32[1] = 0; -+ ll_addr->s6_addr32[2] = addr->s6_addr32[2]; -+ ll_addr->s6_addr32[3] = addr->s6_addr32[3]; -+} -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/halist.c linux-2.4.25/net/ipv6/mobile_ip6/halist.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/halist.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/halist.c 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,507 @@ -+/* -+ * Home Agents List -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#define PREF_BASE 0xffff /* MAX value for u16 field in RA */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/proc_fs.h> -+#include <linux/init.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+ -+#include "hashlist.h" -+#include "util.h" -+#include "debug.h" -+ -+struct mipv6_halist { -+ struct hashlist *entries; -+ struct timer_list expire_timer; -+}; -+ -+static rwlock_t home_agents_lock = RW_LOCK_UNLOCKED; -+ -+static struct mipv6_halist home_agents; -+ -+struct mipv6_halist_entry { -+ struct hashlist_entry e; -+ int ifindex; /* Link identifier */ -+ struct in6_addr link_local_addr; /* HA's link-local address */ -+ struct in6_addr global_addr; /* HA's Global address */ -+ int plen; -+ long preference; /* The preference for this HA */ -+ unsigned long expire; /* expiration time (jiffies) */ -+}; -+ -+static inline void mipv6_ha_ac_add(struct in6_addr *ll_addr, int ifindex, -+ struct in6_addr *glob_addr, int plen) -+{ -+ struct net_device *dev; -+ -+ if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) { -+ struct in6_addr addr; -+ mipv6_ha_anycast(&addr, glob_addr, plen); -+ ipv6_dev_ac_inc(dev, &addr); -+ } -+} -+ -+static inline void mipv6_ha_ac_del(struct in6_addr *ll_addr, int ifindex, -+ struct in6_addr *glob_addr, int plen) -+{ -+ struct net_device *dev; -+ -+ if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) { -+ struct in6_addr addr; -+ mipv6_ha_anycast(&addr, glob_addr, plen); -+ ipv6_dev_ac_dec(dev, &addr); -+ } -+} -+ -+struct preflist_iterator_args { -+ int count; -+ int requested; -+ int ifindex; -+ struct in6_addr *list; -+}; -+ -+static int preflist_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct preflist_iterator_args *state = -+ (struct preflist_iterator_args *)args; -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ struct in6_addr *newaddr = -+ (struct in6_addr *)state->list + state->count; -+ -+ if (state->count >= state->requested) -+ return ITERATOR_STOP; -+ -+ if (time_after(jiffies, entry->expire)) { -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, -+ entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ DEBUG(DBG_INFO, "preflist_iterator: Deleting entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr)); -+ return ITERATOR_DELETE_ENTRY; -+ } -+ if (state->ifindex != entry->ifindex) -+ return ITERATOR_CONT; -+ -+ ipv6_addr_copy(newaddr, &entry->global_addr); -+ DEBUG(DBG_INFO, "preflist_iterator: adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr)); -+ state->count++; -+ -+ return ITERATOR_CONT; -+} -+ -+static int gc_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ -+ int *type = (int *)args; -+ -+ if (*type == 1 || time_after(jiffies, entry->expire)) { -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, -+ entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ return ITERATOR_DELETE_ENTRY; -+ } -+ -+ return ITERATOR_CONT; -+} -+ -+static int mipv6_halist_gc(int type) -+{ -+ DEBUG_FUNC(); -+ hashlist_iterate(home_agents.entries, &type, gc_iterator); -+ return 0; -+} -+ -+static void mipv6_halist_expire(unsigned long dummy) -+{ -+ DEBUG_FUNC(); -+ -+ write_lock(&home_agents_lock); -+ mipv6_halist_gc(0); -+ write_unlock(&home_agents_lock); -+} -+ -+ -+static struct mipv6_halist_entry *mipv6_halist_new_entry(void) -+{ -+ struct mipv6_halist_entry *entry; -+ -+ DEBUG_FUNC(); -+ -+ entry = hashlist_alloc(home_agents.entries, SLAB_ATOMIC); -+ -+ return entry; -+} -+ -+ -+ -+/** -+ * mipv6_halist_add - Add new home agent to the Home Agents List -+ * @ifindex: interface identifier -+ * @glob_addr: home agent's global address -+ * @ll_addr: home agent's link-local address -+ * @pref: relative preference for this home agent -+ * @lifetime: lifetime for the entry -+ * -+ * Adds new home agent to the Home Agents List. The list is interface -+ * specific and @ifindex tells through which interface the home agent -+ * was heard. Returns zero on success and negative on failure. -+ **/ -+ -+int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen, -+ struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime) -+{ -+ int update = 0, ret = 0; -+ unsigned int mpref; -+ struct mipv6_halist_entry *entry = NULL; -+ -+ DEBUG_FUNC(); -+ -+ write_lock(&home_agents_lock); -+ -+ if (glob_addr == NULL || lifetime <= 0) { -+ DEBUG(DBG_WARNING, "invalid arguments"); -+ ret = -EINVAL; -+ goto out; -+ } -+ mpref = PREF_BASE - pref; -+ if ((entry = (struct mipv6_halist_entry *) -+ hashlist_get(home_agents.entries, glob_addr)) != NULL) { -+ if (entry->ifindex == ifindex) { -+ DEBUG(DBG_DATADUMP, "updating old entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr)); -+ update = 1; -+ } else { -+ DEBUG(DBG_INFO, "halist_add : adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr)); -+ update = 0; -+ } -+ } -+ if (update) { -+ entry->expire = jiffies + lifetime * HZ; -+ if (entry->preference != mpref) { -+ entry->preference = mpref; -+ ret = hashlist_reposition(home_agents.entries, -+ (void *)entry, mpref); -+ } -+ } else { -+ entry = mipv6_halist_new_entry(); -+ if (entry == NULL) { -+ DEBUG(DBG_INFO, "list full"); -+ ret = -ENOMEM; -+ goto out; -+ } -+ entry->ifindex = ifindex; -+ if (ll_addr) { -+ ipv6_addr_copy(&entry->link_local_addr, ll_addr); -+ mipv6_ha_ac_add(ll_addr, ifindex, glob_addr, plen); -+ } else -+ ipv6_addr_set(&entry->link_local_addr, 0, 0, 0, 0); -+ -+ ipv6_addr_copy(&entry->global_addr, glob_addr); -+ entry->plen = plen; -+ entry->preference = mpref; -+ entry->expire = jiffies + lifetime * HZ; -+ ret = hashlist_add(home_agents.entries, glob_addr, mpref, -+ entry); -+ } -+out: -+ write_unlock(&home_agents_lock); -+ return ret; -+} -+ -+/** -+ * mipv6_halist_delete - delete home agent from Home Agents List -+ * @glob_addr: home agent's global address -+ * -+ * Deletes entry for home agent @glob_addr from the Home Agent List. -+ **/ -+int mipv6_halist_delete(struct in6_addr *glob_addr) -+{ -+ struct hashlist_entry *e; -+ struct mipv6_halist_entry *entry; -+ DEBUG_FUNC(); -+ -+ if (glob_addr == NULL) { -+ DEBUG(DBG_WARNING, "invalid glob addr"); -+ return -EINVAL; -+ } -+ write_lock(&home_agents_lock); -+ if ((e = hashlist_get(home_agents.entries, glob_addr)) == NULL) { -+ write_unlock(&home_agents_lock); -+ return -ENOENT; -+ } -+ hashlist_delete(home_agents.entries, e); -+ entry = (struct mipv6_halist_entry *)e; -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ hashlist_free(home_agents.entries, e); -+ write_unlock(&home_agents_lock); -+ return 0; -+} -+ -+/** -+ * mipv6_ha_get_pref_list - Get list of preferred home agents -+ * @ifindex: interface identifier -+ * @addrs: pointer to a buffer to store the list -+ * @max: maximum number of home agents to return -+ * -+ * Creates a list of @max preferred (or all known if less than @max) -+ * home agents. Home Agents List is interface specific so you must -+ * supply @ifindex. Stores list in addrs and returns number of home -+ * agents stored. On failure, returns a negative value. -+ **/ -+int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max) -+{ -+ struct preflist_iterator_args args; -+ -+ DEBUG_FUNC(); -+ if (max <= 0) { -+ *addrs = NULL; -+ return 0; -+ } -+ -+ args.count = 0; -+ args.requested = max; -+ args.ifindex = ifindex; -+ args.list = kmalloc(max * sizeof(struct in6_addr), GFP_ATOMIC); -+ -+ if (args.list == NULL) return -ENOMEM; -+ -+ read_lock(&home_agents_lock); -+ hashlist_iterate(home_agents.entries, &args, preflist_iterator); -+ read_unlock(&home_agents_lock); -+ -+ if (args.count >= 0) { -+ *addrs = args.list; -+ } else { -+ kfree(args.list); -+ *addrs = NULL; -+ } -+ -+ return args.count; -+} -+ -+struct getaddr_iterator_args { -+ struct net_device *dev; -+ struct in6_addr *addr; -+}; -+ -+static int getaddr_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ struct getaddr_iterator_args *state = -+ (struct getaddr_iterator_args *)args; -+ -+ if (entry->ifindex != state->dev->ifindex) -+ return ITERATOR_CONT; -+ -+ if (ipv6_chk_addr(&entry->global_addr, state->dev)) { -+ ipv6_addr_copy(state->addr, &entry->global_addr); -+ return ITERATOR_STOP; -+ } -+ return ITERATOR_CONT; -+} -+ -+/* -+ * Get Home Agent Address for given interface. If node is not serving -+ * as a HA for this interface returns negative error value. -+ */ -+int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr) -+{ -+ struct getaddr_iterator_args args; -+ struct net_device *dev; -+ -+ if (ifindex <= 0) -+ return -EINVAL; -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) -+ return -ENODEV; -+ -+ memset(addr, 0, sizeof(struct in6_addr)); -+ args.dev = dev; -+ args.addr = addr; -+ read_lock(&home_agents_lock); -+ hashlist_iterate(home_agents.entries, &args, getaddr_iterator); -+ read_unlock(&home_agents_lock); -+ dev_put(dev); -+ -+ if (ipv6_addr_any(addr)) -+ return -ENOENT; -+ -+ return 0; -+} -+ -+#define HALIST_INFO_LEN 81 -+ -+struct procinfo_iterator_args { -+ char *buffer; -+ int offset; -+ int length; -+ int skip; -+ int len; -+}; -+ -+static int procinfo_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct procinfo_iterator_args *arg = -+ (struct procinfo_iterator_args *)args; -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ unsigned long int expire; -+ -+ DEBUG_FUNC(); -+ -+ if (entry == NULL) return ITERATOR_ERR; -+ -+ if (time_after(jiffies, entry->expire)) { -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, -+ entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ return ITERATOR_DELETE_ENTRY; -+ } -+ if (arg->skip < arg->offset / HALIST_INFO_LEN) { -+ arg->skip++; -+ return ITERATOR_CONT; -+ } -+ -+ if (arg->len >= arg->length) -+ return ITERATOR_CONT; -+ -+ expire = (entry->expire - jiffies) / HZ; -+ -+ arg->len += sprintf(arg->buffer + arg->len, -+ "%02d %08x%08x%08x%08x %08x%08x%08x%08x %05ld %05ld\n", -+ entry->ifindex, -+ ntohl(entry->global_addr.s6_addr32[0]), -+ ntohl(entry->global_addr.s6_addr32[1]), -+ ntohl(entry->global_addr.s6_addr32[2]), -+ ntohl(entry->global_addr.s6_addr32[3]), -+ ntohl(entry->link_local_addr.s6_addr32[0]), -+ ntohl(entry->link_local_addr.s6_addr32[1]), -+ ntohl(entry->link_local_addr.s6_addr32[2]), -+ ntohl(entry->link_local_addr.s6_addr32[3]), -+ -(entry->preference - PREF_BASE), expire); -+ -+ return ITERATOR_CONT; -+} -+ -+static int halist_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct procinfo_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.buffer = buffer; -+ args.offset = offset; -+ args.length = length; -+ args.skip = 0; -+ args.len = 0; -+ -+ read_lock_bh(&home_agents_lock); -+ hashlist_iterate(home_agents.entries, &args, procinfo_iterator); -+ read_unlock_bh(&home_agents_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % HALIST_INFO_LEN; -+ -+ args.len -= offset % HALIST_INFO_LEN; -+ -+ if (args.len > length) -+ args.len = length; -+ if (args.len < 0) -+ args.len = 0; -+ -+ return args.len; -+} -+ -+static int halist_compare(void *data, void *hashkey) -+{ -+ struct mipv6_halist_entry *e = (struct mipv6_halist_entry *)data; -+ struct in6_addr *key = (struct in6_addr *)hashkey; -+ -+ return ipv6_addr_cmp(&e->global_addr, key); -+} -+ -+static __u32 halist_hash(void *hashkey) -+{ -+ struct in6_addr *key = (struct in6_addr *)hashkey; -+ __u32 hash; -+ -+ hash = key->s6_addr32[0] ^ -+ key->s6_addr32[1] ^ -+ key->s6_addr32[2] ^ -+ key->s6_addr32[3]; -+ -+ return hash; -+} -+ -+int __init mipv6_halist_init(__u32 size) -+{ -+ DEBUG_FUNC(); -+ -+ if (size <= 0) { -+ DEBUG(DBG_ERROR, "size must be at least 1"); -+ return -EINVAL; -+ } -+ init_timer(&home_agents.expire_timer); -+ home_agents.expire_timer.data = 0; -+ home_agents.expire_timer.function = mipv6_halist_expire; -+ home_agents_lock = RW_LOCK_UNLOCKED; -+ -+ home_agents.entries = hashlist_create(16, size, sizeof(struct mipv6_halist_entry), -+ "mip6_halist", NULL, NULL, -+ halist_compare, halist_hash); -+ -+ if (home_agents.entries == NULL) { -+ DEBUG(DBG_ERROR, "Failed to initialize hashlist"); -+ return -ENOMEM; -+ } -+ -+ proc_net_create("mip6_home_agents", 0, halist_proc_info); -+ DEBUG(DBG_INFO, "Home Agents List initialized"); -+ return 0; -+} -+ -+void __exit mipv6_halist_exit(void) -+{ -+ DEBUG_FUNC(); -+ proc_net_remove("mip6_home_agents"); -+ write_lock_bh(&home_agents_lock); -+ DEBUG(DBG_INFO, "Stopping the halist timer"); -+ del_timer(&home_agents.expire_timer); -+ mipv6_halist_gc(1); -+ write_unlock_bh(&home_agents_lock); -+ hashlist_destroy(home_agents.entries); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/halist.h linux-2.4.25/net/ipv6/mobile_ip6/halist.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/halist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/halist.h 2004-06-26 11:29:30.000000000 +0100 -@@ -0,0 +1,28 @@ -+/* -+ * MIPL Mobile IPv6 Home Agents List header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HALIST_H -+#define _HALIST_H -+ -+int mipv6_halist_init(__u32 size); -+ -+void mipv6_halist_exit(void); -+ -+int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen, -+ struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime); -+ -+int mipv6_halist_delete(struct in6_addr *glob_addr); -+ -+int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max); -+ -+int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr); -+ -+#endif /* _HALIST_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.c linux-2.4.25/net/ipv6/mobile_ip6/hashlist.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hashlist.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,351 @@ -+/* -+ * Generic hashtable with chaining. Supports secodary sort order -+ * with doubly linked-list. -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id: s.hashlist.c 1.21 02/10/07 19:31:52+03:00 antti@traci.mipl.mediapoli.com $ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ */ -+ -+#include <linux/slab.h> -+#include "hashlist.h" -+#include "debug.h" -+ -+struct hashlist { -+ int count; /* entry count */ -+ int maxcount; /* max entries */ -+ __u32 bucketnum; /* hash buckets */ -+ -+ kmem_cache_t *kmem; -+ -+ struct list_head *hashtable; -+ struct list_head sortedlist; -+ -+ int (*compare)(void *data, void *hashkey); -+ __u32 (*hash_function)(void *hashkey); -+}; -+ -+/** -+ * hashlist_create - Create new hashlist -+ * @bucketnum: number of hash buckets -+ * @maxentries: maximum number of entries (0 = no limit) -+ * @size: entry size in bytes -+ * @name: name for kmem_cache_t -+ * @ctor: kmem_cache_t constructor -+ * @dtor: kmem_cache_t destructor -+ * @compare: compare function for key -+ * @hash_function: hash function -+ * -+ * Creates a hashlist structure with @max_entries entries of @size -+ * bytes. User must supply @hash_function and @compare function for -+ * the hashlist. User can also supply @ctor and @dtor for kmem_cache. -+ **/ -+struct hashlist *hashlist_create(int bucketnum, int max_entries, size_t size, -+ char *name, -+ void (*ctor)(void *, kmem_cache_t *, unsigned long), -+ void (*dtor)(void *, kmem_cache_t *, unsigned long), -+ int (*compare)(void *data, void *hashkey), -+ __u32 (*hash_function)(void *hashkey)) -+{ -+ int i; -+ struct hashlist *hl; -+ -+ if (!compare || !hash_function) -+ goto hlfailed; -+ -+ hl = kmalloc(sizeof(struct hashlist), GFP_ATOMIC); -+ if (!hl) goto hlfailed; -+ -+ hl->kmem = kmem_cache_create(name, size, 0, 0, ctor, dtor); -+ if (!hl->kmem) goto poolfailed; -+ -+ hl->hashtable = kmalloc( -+ sizeof(struct list_head) * bucketnum, GFP_ATOMIC); -+ if (!hl->hashtable) goto hashfailed; -+ -+ for (i = 0; i < bucketnum; i++) -+ INIT_LIST_HEAD(&hl->hashtable[i]); -+ -+ INIT_LIST_HEAD(&hl->sortedlist); -+ -+ hl->maxcount = max_entries; -+ hl->count = 0; -+ hl->bucketnum = bucketnum; -+ hl->compare = compare; -+ hl->hash_function = hash_function; -+ -+ return hl; -+ -+hashfailed: -+ kmem_cache_destroy(hl->kmem); -+ hl->kmem = NULL; -+ -+poolfailed: -+ kfree(hl); -+ -+hlfailed: -+ DEBUG(DBG_ERROR, "could not create hashlist"); -+ -+ return NULL; -+} -+ -+/** -+ * hashlist_destroy - Destroy hashlist -+ * @hashlist: hashlist to destroy -+ * -+ * Frees all memory allocated for a hashlist. -+ **/ -+void hashlist_destroy(struct hashlist *hashlist) -+{ -+ DEBUG_FUNC(); -+ -+ if (hashlist == NULL) return; -+ -+ if (hashlist->hashtable) { -+ kfree(hashlist->hashtable); -+ hashlist->hashtable = NULL; -+ } -+ -+ if (hashlist->kmem) { -+ kmem_cache_destroy(hashlist->kmem); -+ hashlist->kmem = NULL; -+ } -+ -+ kfree(hashlist); -+ -+ return; -+} -+ -+/* -+ * Insert a chain of entries to hashlist into correct order. The -+ * entries are assumed to have valid hashkeys. We use time_after_eq -+ * for comparing, since it handles wrap-around correctly, and the -+ * sortkey is usually jiffies. -+ */ -+static void sorted_insert(struct list_head *lh, struct hashlist_entry *he) -+{ -+ struct list_head *p; -+ struct hashlist_entry *hlp = NULL; -+ unsigned long sortkey = he->sortkey; -+ -+ if (list_empty(lh)) { -+ list_add(&he->sorted, lh); -+ return; -+ } -+ -+ list_for_each(p, lh) { -+ hlp = list_entry(p, typeof(*hlp), sorted); -+ if (time_after_eq(hlp->sortkey, sortkey)) { -+ list_add(&he->sorted, hlp->sorted.prev); -+ return; -+ } -+ } -+ list_add(&he->sorted, &hlp->sorted); -+} -+ -+/** -+ * hashlist_iterate - Apply function for all elements in a hash list -+ * @hashlist: pointer to hashlist -+ * @args: data to pass to the function -+ * @func: pointer to a function -+ * -+ * Apply arbitrary function @func to all elements in a hash list. -+ * @func must be a pointer to a function with the following prototype: -+ * int func(void *entry, void *arg, struct in6_addr *hashkey, unsigned -+ * long *sortkey). Function must return %ITERATOR_STOP, -+ * %ITERATOR_CONT or %ITERATOR_DELETE_ENTRY. %ITERATOR_STOP stops -+ * iterator and returns last return value from the function. -+ * %ITERATOR_CONT continues with iteration. %ITERATOR_DELETE_ENTRY -+ * deletes current entry from the hashlist. If function changes -+ * hashlist element's sortkey, iterator automatically schedules -+ * element to be reinserted after all elements have been processed. -+ */ -+int hashlist_iterate( -+ struct hashlist *hashlist, void *args, -+ hashlist_iterator_t func) -+{ -+ int res = ITERATOR_CONT; -+ unsigned long skey; -+ struct list_head *p, *n, repos; -+ struct hashlist_entry *he; -+ -+ DEBUG_FUNC(); -+ INIT_LIST_HEAD(&repos); -+ -+ list_for_each_safe(p, n, &hashlist->sortedlist) { -+ he = list_entry(p, typeof(*he), sorted); -+ if (res == ITERATOR_STOP) -+ break; -+ skey = he->sortkey; -+ res = func(he, args, &he->sortkey); -+ if (res == ITERATOR_DELETE_ENTRY) { -+ hashlist_delete(hashlist, he); -+ hashlist_free(hashlist, he); -+ } else if (skey != he->sortkey) { -+ /* iterator changed the sortkey, schedule for -+ * repositioning */ -+ list_move(&he->sorted, &repos); -+ } -+ } -+ list_for_each_safe(p, n, &repos) { -+ he = list_entry(p, typeof(*he), sorted); -+ sorted_insert(&hashlist->sortedlist, he); -+ } -+ return res; -+} -+ -+/** -+ * hashlist_alloc - Allocate memory for a hashlist entry -+ * @hashlist: hashlist for allocated entry -+ * @size: size of entry in bytes -+ * -+ * Allocates @size bytes memory from @hashlist->kmem. -+ **/ -+void *hashlist_alloc(struct hashlist *hashlist, int type) -+{ -+ if (hashlist == NULL) return NULL; -+ return kmem_cache_alloc(hashlist->kmem, type); -+} -+ -+/** -+ * hashlist_free - Free hashlist entry -+ * @hashlist: hashlist where @he is -+ * @he: entry to free -+ * -+ * Frees an allocated hashlist entry. -+ **/ -+void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he) -+{ -+ kmem_cache_free(hashlist->kmem, he); -+} -+ -+/** -+ * hashlist_add - Add element to hashlist -+ * @hashlist: pointer to hashlist -+ * @hashkey: hashkey for the element -+ * @sortkey: key for sorting -+ * @data: element data -+ * -+ * Add element to hashlist. Hashlist is also sorted in a linked list -+ * by @sortkey. -+ */ -+int hashlist_add(struct hashlist *hashlist, void *hashkey, -+ unsigned long sortkey, void *entry) -+{ -+ struct hashlist_entry *he = (struct hashlist_entry *)entry; -+ unsigned int hash; -+ -+ if (hashlist->count >= hashlist->maxcount) -+ return -1; -+ -+ hashlist->count++; -+ -+ /* link the entry to sorted order */ -+ he->sortkey = sortkey; -+ sorted_insert(&hashlist->sortedlist, he); -+ -+ /* hash the entry */ -+ hash = hashlist->hash_function(hashkey) % hashlist->bucketnum; -+ list_add(&he->hashlist, &hashlist->hashtable[hash]); -+ -+ return 0; -+} -+ -+/** -+ * hashlist_get_ex - Get element from hashlist -+ * @hashlist: hashlist -+ * @hashkey: hashkey of the desired entry -+ * -+ * Lookup entry with @hashkey from the hash table using @compare -+ * function for entry comparison. Returns entry on success, otherwise -+ * %NULL. -+ **/ -+struct hashlist_entry *hashlist_get_ex( -+ struct hashlist *hashlist, void *hashkey, -+ int (*compare)(void *data, void *hashkey)) -+{ -+ struct list_head *p, *bkt; -+ __u32 hash; -+ -+ hash = hashlist->hash_function(hashkey) % hashlist->bucketnum; -+ bkt = &hashlist->hashtable[hash]; -+ -+ /* scan the entries within the same hashbucket */ -+ list_for_each(p, bkt) { -+ struct hashlist_entry *he = list_entry(p, typeof(*he), -+ hashlist); -+ if (compare(he, hashkey) == 0) -+ return he; -+ } -+ -+ return NULL; -+} -+ -+/** -+ * hashlist_get - Get element from hashlist -+ * @hashlist: hashlist -+ * @hashkey: hashkey of the desired entry -+ * -+ * Lookup entry with @hashkey from the hash table. Returns entry on -+ * success, otherwise %NULL. -+ **/ -+struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey) -+{ -+ return hashlist_get_ex(hashlist, hashkey, hashlist->compare); -+} -+ -+/** -+ * hashlist_reposition - set entry to new position in the list -+ * @hashlist: hashlist -+ * @he: entry to reposition -+ * @sortkey: new sortkey of the entry -+ * -+ * If secondary order sortkey changes, entry must be repositioned in -+ * the sorted list. -+ **/ -+int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he, -+ unsigned long sortkey) -+{ -+ list_del(&he->sorted); -+ he->sortkey = sortkey; -+ sorted_insert(&hashlist->sortedlist, he); -+ -+ return 0; -+} -+ -+/** -+ * hashlist_delete - Delete entry from hashlist -+ * @hashlist: hashlist where entry is -+ * @he: entry to delete -+ * -+ * Deletes an entry from the hashlist and sorted list. -+ **/ -+void hashlist_delete(struct hashlist *hashlist, -+ struct hashlist_entry *he) -+{ -+ list_del_init(&he->hashlist); -+ list_del_init(&he->sorted); -+ -+ hashlist->count--; -+} -+ -+/** -+ * hashlist_get_first - Get first item from sorted list -+ * @hashlist: pointer to hashlist -+ * -+ * Returns first item in the secondary sort order. -+ **/ -+void * hashlist_get_first(struct hashlist *hashlist) -+{ -+ if (list_empty(&hashlist->sortedlist)) -+ return NULL; -+ -+ return list_entry(hashlist->sortedlist.next, struct hashlist_entry, sorted); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.h linux-2.4.25/net/ipv6/mobile_ip6/hashlist.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hashlist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hashlist.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,63 @@ -+/* -+ * MIPL Mobile IPv6 Hashlist header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HASHLIST_H -+#define _HASHLIST_H -+ -+#define ITERATOR_ERR -1 -+#define ITERATOR_CONT 0 -+#define ITERATOR_STOP 1 -+#define ITERATOR_DELETE_ENTRY 2 -+ -+struct kmem_cache_t; -+ -+struct hashlist_entry { -+ unsigned long sortkey; -+ struct list_head sorted; -+ struct list_head hashlist; -+}; -+ -+struct hashlist * hashlist_create( -+ int bucketnum, int max_entries, size_t size, char *name, -+ void (*ctor)(void *, kmem_cache_t *, unsigned long), -+ void (*dtor)(void *, kmem_cache_t *, unsigned long), -+ int (*compare)(void *data, void *hashkey), -+ __u32 (*hash_function)(void *hashkey)); -+ -+void hashlist_destroy(struct hashlist *hashlist); -+ -+void *hashlist_alloc(struct hashlist *hashlist, int type); -+ -+void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he); -+ -+struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey); -+ -+struct hashlist_entry *hashlist_get_ex( -+ struct hashlist *hashlist, void *hashkey, -+ int (*compare)(void *data, void *hashkey)); -+ -+int hashlist_add(struct hashlist *hashlist, void *hashkey, -+ unsigned long sortkey, void *data); -+ -+void hashlist_delete(struct hashlist *hashlist, struct hashlist_entry *he); -+ -+/* iterator function */ -+typedef int (*hashlist_iterator_t)(void *, void *, unsigned long *); -+ -+int hashlist_iterate(struct hashlist *hashlist, void *args, -+ hashlist_iterator_t func); -+ -+void * hashlist_get_first(struct hashlist *hashlist); -+ -+int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he, -+ unsigned long sortkey); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.c linux-2.4.25/net/ipv6/mobile_ip6/hmac.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hmac.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,658 @@ -+/* Authentication algorithms -+ * -+ * Authors: -+ * Alexis Olivereau <Alexis.Olivereau@crm.mot.com> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Changes: -+ * Henrik Petander : Cleaned up unused parts -+ * -+ */ -+ -+#include <linux/sched.h> -+#include <linux/tty.h> -+#include <linux/types.h> -+#include <linux/slab.h> -+#include <linux/in6.h> -+ -+#include "hmac.h" -+#define LROLL(x, s) (((x) << (s)) | ((x) >> (32 - (s)))) -+ -+/* MD5 */ -+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) -+#define H(x, y, z) ((x) ^ (y) ^ (z)) -+#define I(x, y, z) ((y) ^ ((x) | ~(z))) -+ -+#define FF(a, b, c, d, m, s, t) { \ -+ (a) += F ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+#define GG(a, b, c, d, m, s, t) { \ -+ (a) += G ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+#define HH(a, b, c, d, m, s, t) { \ -+ (a) += H ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+#define II(a, b, c, d, m, s, t) { \ -+ (a) += I ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+ -+#define s11 7 -+#define s12 12 -+#define s13 17 -+#define s14 22 -+#define s21 5 -+#define s22 9 -+#define s23 14 -+#define s24 20 -+#define s31 4 -+#define s32 11 -+#define s33 16 -+#define s34 23 -+#define s41 6 -+#define s42 10 -+#define s43 15 -+#define s44 21 -+ -+/* SHA-1 */ -+#define f(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -+#define g(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) -+#define h(x, y, z) ((x) ^ (y) ^ (z)) -+ -+#define K1 0x5a827999 -+#define K2 0x6ed9eba1 -+#define K3 0x8f1bbcdc -+#define K4 0xca62c1d6 -+ -+int ah_hmac_md5_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len) -+{ -+ int i; -+ int key_up4; -+ uint32_t ipad = 0x36363636; -+ uint8_t extkey[64]; -+ -+ ahp->key_auth = key; -+ ahp->key_auth_len = key_len; -+ ahp->context = (void *) kmalloc(sizeof(MD5_CTX), GFP_ATOMIC); -+ if (ahp->context == NULL) -+ return -1; -+ md5_init((MD5_CTX *) ahp->context); -+ if ((64 * sizeof(uint8_t)) < ahp->key_auth_len) { -+ printk("buffer overflow!"); -+ return -1; -+ } -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = ipad; -+ -+ md5_compute((MD5_CTX *) ahp->context, extkey, 64); -+ return 0; -+} -+ -+void ah_hmac_md5_loop(struct ah_processing *ahp, void *str, uint32_t len) -+{ -+ md5_compute((MD5_CTX *) ahp->context, str, len); -+} -+ -+void ah_hmac_md5_result(struct ah_processing *ahp, char *digest) -+{ -+ uint8_t inner[HMAC_MD5_HASH_LEN]; -+ int i; -+ int key_up4; -+ uint32_t opad = 0x5c5c5c5c; -+ uint8_t extkey[64]; -+ -+ md5_final((MD5_CTX *) ahp->context, inner); -+ md5_init((MD5_CTX *) ahp->context); -+ -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = opad; -+ -+ md5_compute((MD5_CTX *) ahp->context, extkey, 64); -+ md5_compute((MD5_CTX *) ahp->context, inner, HMAC_MD5_HASH_LEN); -+ -+ md5_final((MD5_CTX *) ahp->context, digest); -+ -+ kfree(ahp->context); -+} -+ -+int ah_hmac_sha1_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len) -+{ -+ int i; -+ int key_up4; -+ uint32_t ipad = 0x36363636; -+ uint8_t extkey[64]; -+ -+ ahp->key_auth = key; -+ ahp->key_auth_len = key_len; -+ -+ ahp->context = (void *) kmalloc(sizeof(SHA1_CTX), GFP_ATOMIC); -+ //if (ahp->context == NULL) -+ // return -1; -+ -+ sha1_init((SHA1_CTX *) ahp->context); -+ -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = ipad; -+ -+ sha1_compute((SHA1_CTX *) ahp->context, extkey, 64); -+ return 0; -+} -+ -+void ah_hmac_sha1_loop(struct ah_processing *ahp, void *str, uint32_t len) -+{ -+ if (!ahp) -+ return; -+ sha1_compute((SHA1_CTX *) ahp->context, str, len); -+} -+ -+void ah_hmac_sha1_result(struct ah_processing *ahp, char *digest) -+{ -+ uint8_t inner[HMAC_SHA1_HASH_LEN]; -+ int i; -+ int key_up4; -+ uint32_t opad = 0x5c5c5c5c; -+ uint8_t extkey[64]; -+ -+ if (!ahp) -+ return; -+ sha1_final((SHA1_CTX *) ahp->context, inner); -+ sha1_init((SHA1_CTX *) ahp->context); -+ -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = opad; -+ -+ sha1_compute((SHA1_CTX *) ahp->context, extkey, 64); -+ sha1_compute((SHA1_CTX *) ahp->context, inner, -+ HMAC_SHA1_HASH_LEN); -+ -+ sha1_final((SHA1_CTX *) ahp->context, digest); -+ -+ kfree(ahp->context); -+} -+ -+void md5_init(MD5_CTX * ctx) -+{ -+ ctx->A = 0x67452301; -+ ctx->B = 0xefcdab89; -+ ctx->C = 0x98badcfe; -+ ctx->D = 0x10325476; -+ ctx->buf_cur = ctx->buf; -+ ctx->bitlen[0] = ctx->bitlen[1] = 0; -+ memset(ctx->buf, 0, 64); -+} -+ -+void md5_over_block(MD5_CTX * ctx, uint8_t * data) -+{ -+ uint32_t M[16]; -+ uint32_t a = ctx->A; -+ uint32_t b = ctx->B; -+ uint32_t c = ctx->C; -+ uint32_t d = ctx->D; -+ -+ create_M_blocks(M, data); -+ -+ /* Round 1 */ -+ FF(a, b, c, d, M[0], s11, 0xd76aa478); /* 1 */ -+ FF(d, a, b, c, M[1], s12, 0xe8c7b756); /* 2 */ -+ FF(c, d, a, b, M[2], s13, 0x242070db); /* 3 */ -+ FF(b, c, d, a, M[3], s14, 0xc1bdceee); /* 4 */ -+ FF(a, b, c, d, M[4], s11, 0xf57c0faf); /* 5 */ -+ FF(d, a, b, c, M[5], s12, 0x4787c62a); /* 6 */ -+ FF(c, d, a, b, M[6], s13, 0xa8304613); /* 7 */ -+ FF(b, c, d, a, M[7], s14, 0xfd469501); /* 8 */ -+ FF(a, b, c, d, M[8], s11, 0x698098d8); /* 9 */ -+ FF(d, a, b, c, M[9], s12, 0x8b44f7af); /* 10 */ -+ FF(c, d, a, b, M[10], s13, 0xffff5bb1); /* 11 */ -+ FF(b, c, d, a, M[11], s14, 0x895cd7be); /* 12 */ -+ FF(a, b, c, d, M[12], s11, 0x6b901122); /* 13 */ -+ FF(d, a, b, c, M[13], s12, 0xfd987193); /* 14 */ -+ FF(c, d, a, b, M[14], s13, 0xa679438e); /* 15 */ -+ FF(b, c, d, a, M[15], s14, 0x49b40821); /* 16 */ -+ -+ /* Round 2 */ -+ GG(a, b, c, d, M[1], s21, 0xf61e2562); /* 17 */ -+ GG(d, a, b, c, M[6], s22, 0xc040b340); /* 18 */ -+ GG(c, d, a, b, M[11], s23, 0x265e5a51); /* 19 */ -+ GG(b, c, d, a, M[0], s24, 0xe9b6c7aa); /* 20 */ -+ GG(a, b, c, d, M[5], s21, 0xd62f105d); /* 21 */ -+ GG(d, a, b, c, M[10], s22, 0x02441453); /* 22 */ -+ GG(c, d, a, b, M[15], s23, 0xd8a1e681); /* 23 */ -+ GG(b, c, d, a, M[4], s24, 0xe7d3fbc8); /* 24 */ -+ GG(a, b, c, d, M[9], s21, 0x21e1cde6); /* 25 */ -+ GG(d, a, b, c, M[14], s22, 0xc33707d6); /* 26 */ -+ GG(c, d, a, b, M[3], s23, 0xf4d50d87); /* 27 */ -+ GG(b, c, d, a, M[8], s24, 0x455a14ed); /* 28 */ -+ GG(a, b, c, d, M[13], s21, 0xa9e3e905); /* 29 */ -+ GG(d, a, b, c, M[2], s22, 0xfcefa3f8); /* 30 */ -+ GG(c, d, a, b, M[7], s23, 0x676f02d9); /* 31 */ -+ GG(b, c, d, a, M[12], s24, 0x8d2a4c8a); /* 32 */ -+ -+ /* Round 3 */ -+ HH(a, b, c, d, M[5], s31, 0xfffa3942); /* 33 */ -+ HH(d, a, b, c, M[8], s32, 0x8771f681); /* 34 */ -+ HH(c, d, a, b, M[11], s33, 0x6d9d6122); /* 35 */ -+ HH(b, c, d, a, M[14], s34, 0xfde5380c); /* 36 */ -+ HH(a, b, c, d, M[1], s31, 0xa4beea44); /* 37 */ -+ HH(d, a, b, c, M[4], s32, 0x4bdecfa9); /* 38 */ -+ HH(c, d, a, b, M[7], s33, 0xf6bb4b60); /* 39 */ -+ HH(b, c, d, a, M[10], s34, 0xbebfbc70); /* 40 */ -+ HH(a, b, c, d, M[13], s31, 0x289b7ec6); /* 41 */ -+ HH(d, a, b, c, M[0], s32, 0xeaa127fa); /* 42 */ -+ HH(c, d, a, b, M[3], s33, 0xd4ef3085); /* 43 */ -+ HH(b, c, d, a, M[6], s34, 0x4881d05); /* 44 */ -+ HH(a, b, c, d, M[9], s31, 0xd9d4d039); /* 45 */ -+ HH(d, a, b, c, M[12], s32, 0xe6db99e5); /* 46 */ -+ HH(c, d, a, b, M[15], s33, 0x1fa27cf8); /* 47 */ -+ HH(b, c, d, a, M[2], s34, 0xc4ac5665); /* 48 */ -+ -+ /* Round 4 */ -+ II(a, b, c, d, M[0], s41, 0xf4292244); /* 49 */ -+ II(d, a, b, c, M[7], s42, 0x432aff97); /* 50 */ -+ II(c, d, a, b, M[14], s43, 0xab9423a7); /* 51 */ -+ II(b, c, d, a, M[5], s44, 0xfc93a039); /* 52 */ -+ II(a, b, c, d, M[12], s41, 0x655b59c3); /* 53 */ -+ II(d, a, b, c, M[3], s42, 0x8f0ccc92); /* 54 */ -+ II(c, d, a, b, M[10], s43, 0xffeff47d); /* 55 */ -+ II(b, c, d, a, M[1], s44, 0x85845dd1); /* 56 */ -+ II(a, b, c, d, M[8], s41, 0x6fa87e4f); /* 57 */ -+ II(d, a, b, c, M[15], s42, 0xfe2ce6e0); /* 58 */ -+ II(c, d, a, b, M[6], s43, 0xa3014314); /* 59 */ -+ II(b, c, d, a, M[13], s44, 0x4e0811a1); /* 60 */ -+ II(a, b, c, d, M[4], s41, 0xf7537e82); /* 61 */ -+ II(d, a, b, c, M[11], s42, 0xbd3af235); /* 62 */ -+ II(c, d, a, b, M[2], s43, 0x2ad7d2bb); /* 63 */ -+ II(b, c, d, a, M[9], s44, 0xeb86d391); /* 64 */ -+ -+ ctx->A += a; -+ ctx->B += b; -+ ctx->C += c; -+ ctx->D += d; -+} -+ -+void create_M_blocks(uint32_t * M, uint8_t * data) -+{ -+#ifdef HAVE_LITTLE_ENDIAN -+ memcpy((uint8_t *) M, data, 64); -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+#ifdef HAVE_BIG_ENDIAN -+ int i; -+ for (i = 0; i < 16; i++, data += 4) { -+ ((uint8_t *) (&M[i]))[0] = data[3]; -+ ((uint8_t *) (&M[i]))[1] = data[2]; -+ ((uint8_t *) (&M[i]))[2] = data[1]; -+ ((uint8_t *) (&M[i]))[3] = data[0]; -+ } -+#endif /* HAVE_BIG_ENDIAN */ -+} -+ -+void md5_compute(MD5_CTX * ctx, uint8_t * data, uint32_t len) -+{ -+ uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f); -+ -+ /* First we update the bit length */ -+ if ((ctx->bitlen[0] += (len << 3)) < (len << 3)) -+ ctx->bitlen[1]++; -+ ctx->bitlen[1] += (len >> 29); /* len is expressed in bytes */ -+ -+ if (pos) { -+ /* Buffer is not empty */ -+ if (64 - pos >= len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ pos += len; -+ if (pos == 64) { -+ /* The current block is over */ -+ md5_over_block(ctx, ctx->buf); -+ ctx->buf_cur = ctx->buf; -+ } -+ return; -+ } else { -+ memcpy(ctx->buf_cur, data, 64 - pos); -+ md5_over_block(ctx, ctx->buf); -+ len -= (64 - pos); -+ data += (64 - pos); -+ ctx->buf_cur = ctx->buf; -+ } -+ } -+ while (len >= 64) { -+ md5_over_block(ctx, data); -+ len -= 64; -+ data += 64; -+ } -+ if (len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ } -+} -+ -+void md5_final(MD5_CTX * ctx, uint8_t * digest) -+{ -+ uint32_t rem_size; -+ uint8_t *buf_cur = ctx->buf_cur; -+ int i; -+ -+ rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f); -+ *(buf_cur++) = 0x80; -+ -+ if (rem_size > 8 + 1) { -+ /* We have enough room in the current block */ -+ for (i = 0; i < rem_size - 8 - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ } else { -+ /* We do not have enough room and need therefore to add a new -+ 64-byte block */ -+ for (i = 0; i < rem_size - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ md5_over_block(ctx, ctx->buf); -+ -+ buf_cur = ctx->buf; -+ for (i = 0; i < 64 - 8; i++) { -+ *(buf_cur++) = 0; -+ } -+ } -+#ifdef HAVE_LITTLE_ENDIAN -+ memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8); -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+#ifdef HAVE_BIG_ENDIAN -+ *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff; -+#endif /* HAVE_BIG_ENDIAN */ -+ -+ md5_over_block(ctx, ctx->buf); -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t)); -+ memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t)); -+ memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t)); -+ memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t)); -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+#ifdef HAVE_BIG_ENDIAN -+ digest[0] = ((ctx->A) >> 24) & 0xff; -+ digest[1] = ((ctx->A) >> 16) & 0xff; -+ digest[2] = ((ctx->A) >> 8) & 0xff; -+ digest[3] = ((ctx->A) >> 0) & 0xff; -+ digest[4] = ((ctx->B) >> 24) & 0xff; -+ digest[5] = ((ctx->B) >> 16) & 0xff; -+ digest[6] = ((ctx->B) >> 8) & 0xff; -+ digest[7] = ((ctx->B) >> 0) & 0xff; -+ digest[8] = ((ctx->C) >> 24) & 0xff; -+ digest[9] = ((ctx->C) >> 16) & 0xff; -+ digest[10] = ((ctx->C) >> 8) & 0xff; -+ digest[11] = ((ctx->C) >> 0) & 0xff; -+ digest[12] = ((ctx->D) >> 24) & 0xff; -+ digest[13] = ((ctx->D) >> 16) & 0xff; -+ digest[14] = ((ctx->D) >> 8) & 0xff; -+ digest[15] = ((ctx->D) >> 0) & 0xff; -+#endif /* HAVE_BIG_ENDIAN */ -+} -+ -+void sha1_init(SHA1_CTX * ctx) -+{ -+ ctx->A = 0x67452301; -+ ctx->B = 0xefcdab89; -+ ctx->C = 0x98badcfe; -+ ctx->D = 0x10325476; -+ ctx->E = 0xc3d2e1f0; -+ ctx->buf_cur = ctx->buf; -+ ctx->bitlen[0] = ctx->bitlen[1] = 0; -+ memset(ctx->buf, 0, 64); -+} -+ -+void sha1_over_block(SHA1_CTX * ctx, uint8_t * data) -+{ -+ int i; -+ uint32_t W[80]; -+ uint32_t a = ctx->A; -+ uint32_t b = ctx->B; -+ uint32_t c = ctx->C; -+ uint32_t d = ctx->D; -+ uint32_t e = ctx->E; -+ uint32_t temp; -+ -+ create_W_blocks(W, data); -+ -+ /* Round 1 */ -+ for (i = 0; i < 20; i++) { -+ temp = LROLL(a, 5) + f(b, c, d) + e + W[i] + K1; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ /* Round 2 */ -+ for (i = 20; i < 40; i++) { -+ temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K2; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ /* Round 3 */ -+ for (i = 40; i < 60; i++) { -+ temp = LROLL(a, 5) + g(b, c, d) + e + W[i] + K3; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ /* Round 4 */ -+ for (i = 60; i < 80; i++) { -+ temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K4; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ ctx->A += a; -+ ctx->B += b; -+ ctx->C += c; -+ ctx->D += d; -+ ctx->E += e; -+} -+ -+void create_W_blocks(uint32_t * W, uint8_t * data) -+{ -+ int i; -+ -+#ifdef HAVE_BIG_ENDIAN -+ memcpy((uint8_t *) W, data, 64); -+#endif /* HAVE_BIG_ENDIAN */ -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ for (i = 0; i < 16; i++, data += 4) { -+ ((uint8_t *) (&W[i]))[0] = data[3]; -+ ((uint8_t *) (&W[i]))[1] = data[2]; -+ ((uint8_t *) (&W[i]))[2] = data[1]; -+ ((uint8_t *) (&W[i]))[3] = data[0]; -+ } -+#endif /* HAVE_LITTLE_ENDIAN */ -+ for (i = 16; i < 80; i++) { -+ W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; -+ W[i] = LROLL(W[i], 1); -+ } -+} -+ -+void sha1_compute(SHA1_CTX * ctx, uint8_t * data, uint32_t len) -+{ -+ uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f); -+ -+ /* First we update the bit length */ -+ if ((ctx->bitlen[0] += (len << 3)) < (len << 3)) -+ ctx->bitlen[1]++; -+ ctx->bitlen[1] += (len >> 29); /* len is expressed in bytes */ -+ -+ if (pos) { -+ /* Buffer is not empty */ -+ if (64 - pos >= len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ pos += len; -+ if (pos == 64) { -+ /* The current block is over */ -+ sha1_over_block(ctx, ctx->buf); -+ ctx->buf_cur = ctx->buf; -+ } -+ return; -+ } else { -+ memcpy(ctx->buf_cur, data, 64 - pos); -+ sha1_over_block(ctx, ctx->buf); -+ len -= (64 - pos); -+ data += (64 - pos); -+ ctx->buf_cur = ctx->buf; -+ } -+ } -+ while (len >= 64) { -+ sha1_over_block(ctx, data); -+ len -= 64; -+ data += 64; -+ } -+ if (len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ } -+} -+ -+void sha1_final(SHA1_CTX * ctx, uint8_t * digest) -+{ -+ uint32_t rem_size; -+ uint8_t *buf_cur = ctx->buf_cur; -+ int i; -+ -+ rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f); -+ *(buf_cur++) = 0x80; -+ -+ if (rem_size > 8 + 1) { -+ /* We have enough room in the current block */ -+ for (i = 0; i < rem_size - 8 - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ } else { -+ /* We do not have enough room and need therefore to add a new -+ 64-byte block */ -+ for (i = 0; i < rem_size - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ sha1_over_block(ctx, ctx->buf); -+ -+ buf_cur = ctx->buf; -+ for (i = 0; i < 64 - 8; i++) { -+ *(buf_cur++) = 0; -+ } -+ } -+#ifdef HAVE_BIG_ENDIAN -+ memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8); -+#endif /* HAVE_BIG_ENDIAN */ -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff; -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+ sha1_over_block(ctx, ctx->buf); -+ -+#ifdef HAVE_BIG_ENDIAN -+ memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t)); -+ memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t)); -+ memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t)); -+ memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t)); -+ memcpy(digest + 16, (uint8_t *) (&(ctx->E)), sizeof(uint32_t)); -+#endif /* HAVE_BIG_ENDIAN */ -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ digest[0] = ((ctx->A) >> 24) & 0xff; -+ digest[1] = ((ctx->A) >> 16) & 0xff; -+ digest[2] = ((ctx->A) >> 8) & 0xff; -+ digest[3] = ((ctx->A) >> 0) & 0xff; -+ digest[4] = ((ctx->B) >> 24) & 0xff; -+ digest[5] = ((ctx->B) >> 16) & 0xff; -+ digest[6] = ((ctx->B) >> 8) & 0xff; -+ digest[7] = ((ctx->B) >> 0) & 0xff; -+ digest[8] = ((ctx->C) >> 24) & 0xff; -+ digest[9] = ((ctx->C) >> 16) & 0xff; -+ digest[10] = ((ctx->C) >> 8) & 0xff; -+ digest[11] = ((ctx->C) >> 0) & 0xff; -+ digest[12] = ((ctx->D) >> 24) & 0xff; -+ digest[13] = ((ctx->D) >> 16) & 0xff; -+ digest[14] = ((ctx->D) >> 8) & 0xff; -+ digest[15] = ((ctx->D) >> 0) & 0xff; -+ digest[16] = ((ctx->E) >> 24) & 0xff; -+ digest[17] = ((ctx->E) >> 16) & 0xff; -+ digest[18] = ((ctx->E) >> 8) & 0xff; -+ digest[19] = ((ctx->E) >> 0) & 0xff; -+#endif /* HAVE_LITTLE_ENDIAN */ -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.h linux-2.4.25/net/ipv6/mobile_ip6/hmac.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/hmac.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/hmac.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,94 @@ -+/* -+ * MIPL Mobile IPv6 Message authentication algorithms -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HMAC_H -+#define _HMAC_H -+ -+#include <linux/types.h> -+#include <linux/in6.h> -+ -+#define HAVE_LITTLE_ENDIAN -+ -+#define NO_EXPIRY 1 /* For sec_as */ -+ -+#define ALG_AUTH_NONE 0 -+#define ALG_AUTH_HMAC_MD5 1 -+#define ALG_AUTH_HMAC_SHA1 2 -+ -+struct sec_as; -+struct ah_processing { -+ void *context; -+ struct sec_as *sas; -+ u_int8_t *key_auth; -+ u_int32_t key_auth_len; -+}; -+ -+struct antireplay { -+ u_int32_t count; -+ u_int32_t bitmap; -+}; -+ -+typedef struct { -+ u_int32_t A, B, C, D; -+ u_int32_t bitlen[2]; -+ u_int8_t* buf_cur; -+ u_int8_t buf[64]; -+} MD5_CTX; -+ -+typedef struct { -+ u_int32_t A, B, C, D, E; -+ u_int32_t bitlen[2]; -+ u_int8_t* buf_cur; -+ u_int8_t buf[64]; -+} SHA1_CTX; -+ -+ -+ -+int ah_hmac_md5_init (struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len); -+void ah_hmac_md5_loop(struct ah_processing*, void*, u_int32_t); -+void ah_hmac_md5_result(struct ah_processing*, char*); -+int ah_hmac_sha1_init(struct ah_processing*, u_int8_t *key, u_int32_t key_len); -+void ah_hmac_sha1_loop(struct ah_processing*, void*, u_int32_t); -+void ah_hmac_sha1_result(struct ah_processing*, char*); -+ -+ -+#define AH_HDR_LEN 12 /* # of bytes for Next Header, Payload Length, -+ RESERVED, Security Parameters Index and -+ -+ Sequence Number Field */ -+ -+void md5_init(MD5_CTX *ctx); -+void md5_over_block(MD5_CTX *ctx, u_int8_t* data); -+void create_M_blocks(u_int32_t* M, u_int8_t* data); -+void md5_compute(MD5_CTX *ctx, u_int8_t* data, u_int32_t len); -+void md5_final(MD5_CTX *ctx, u_int8_t* digest); -+ -+void sha1_init(SHA1_CTX *ctx); -+void sha1_over_block(SHA1_CTX *ctx, u_int8_t* data); -+void create_W_blocks(u_int32_t* W, u_int8_t* data); -+void sha1_compute(SHA1_CTX *ctx, u_int8_t* data, u_int32_t len); -+void sha1_final(SHA1_CTX *ctx, u_int8_t* digest); -+ -+struct mipv6_acq { -+ struct in6_addr coa; -+ struct in6_addr haddr; -+ struct in6_addr peer; -+ u_int32_t spi; -+}; -+#define MIPV6_MAX_AUTH_DATA 20 -+ -+#define HMAC_MD5_HASH_LEN 16 -+#define HMAC_SHA1_HASH_LEN 20 -+#define HMAC_SHA1_KEY_SIZE 20 -+#define HMAC_MD5_ICV_LEN 12 /* RFC 2403 */ -+#define HMAC_SHA1_ICV_LEN 12 /* RFC 2404 */ -+ -+#endif /* _HMAC_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ioctl_mn.c linux-2.4.25/net/ipv6/mobile_ip6/ioctl_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ioctl_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ioctl_mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,142 @@ -+/* -+ * Mobile Node IOCTL Control device -+ * -+ * Authors: -+ * Henrik Petander <lpetande@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/fs.h> -+#include <linux/poll.h> -+#include <linux/ioctl.h> -+#include <net/ipv6.h> -+#include <asm/uaccess.h> -+ -+#include "debug.h" -+#include "mdetect.h" -+#include "multiaccess_ctl.h" -+ -+/* Reserved for local / experimental use */ -+#define MAJOR_NUM 0xf9 -+ -+/* Get Care-of address information for Mobile Node */ -+#define IOCTL_GET_CAREOFADDR _IOWR(MAJOR_NUM, 9, void *) -+ -+#define MA_IOCTL_SET_IFACE_PREFERENCE _IOR (MAJOR_NUM, 13, void *) -+ -+/* The name of the device file */ -+#define CTLFILE "mipv6_dev" -+ -+static int inuse = 0; -+ -+static int mipv6_open(struct inode *inode, struct file *file) -+{ -+ DEBUG(DBG_INFO, "(%p)\n", file); -+ -+ if (inuse) -+ return -EBUSY; -+ -+ inuse++; -+ -+ MOD_INC_USE_COUNT; -+ -+ return 0; -+} -+ -+static int mipv6_close(struct inode *inode, struct file *file) -+{ -+ DEBUG(DBG_INFO, "(%p,%p)\n", inode, file); -+ inuse--; -+ -+ MOD_DEC_USE_COUNT; -+ -+ return 0; -+} -+ -+int mipv6_ioctl(struct inode *inode, struct file *file, -+ unsigned int ioctl_num, /* The number of the ioctl */ -+ unsigned long arg) /* The parameter to it */ -+{ -+ struct in6_addr careofaddr; -+ -+ /* Switch according to the ioctl called */ -+ switch (ioctl_num) { -+ case IOCTL_GET_CAREOFADDR: -+ DEBUG(DBG_DATADUMP, "IOCTL_GET_CAREOFADDR"); -+ /* First get home address from user and then look up -+ * the care-of address and return it -+ */ -+ if (copy_from_user(&careofaddr, (struct in6_addr *)arg, -+ sizeof(struct in6_addr)) < 0) { -+ DEBUG(DBG_WARNING, "Copy from user failed"); -+ return -EFAULT; -+ } -+ mipv6_get_care_of_address(&careofaddr, &careofaddr); -+ if (copy_to_user((struct in6_addr *)arg, &careofaddr, -+ sizeof(struct in6_addr)) < 0) { -+ DEBUG(DBG_WARNING, "copy_to_user failed"); -+ return -EFAULT; -+ } -+ break; -+ case MA_IOCTL_SET_IFACE_PREFERENCE: -+ DEBUG(DBG_INFO, "MA_IOCTL_SET_IFACE_PREFERENCE"); -+ ma_ctl_set_preference(arg); -+ break; -+ -+ default: -+ DEBUG(DBG_WARNING, "Unknown ioctl cmd (%d)", ioctl_num); -+ return -ENOENT; -+ } -+ return 0; -+} -+ -+struct file_operations Fops = { -+ owner: THIS_MODULE, -+ read: NULL, -+ write: NULL, -+ poll: NULL, -+ ioctl: mipv6_ioctl, -+ open: mipv6_open, -+ release: mipv6_close -+}; -+ -+ -+/* Initialize the module - Register the character device */ -+int mipv6_ioctl_mn_init(void) -+{ -+ int ret_val; -+ -+ /* Register the character device (atleast try) */ -+ ret_val = register_chrdev(MAJOR_NUM, CTLFILE, &Fops); -+ -+ /* Negative values signify an error */ -+ if (ret_val < 0) { -+ DEBUG(DBG_ERROR, "failed registering char device (err=%d)", -+ ret_val); -+ return ret_val; -+ } -+ -+ DEBUG(DBG_INFO, "Device number %x, success", MAJOR_NUM); -+ return 0; -+} -+ -+ -+/* Cleanup - unregister the appropriate file from /proc */ -+void mipv6_ioctl_mn_exit(void) -+{ -+ int ret; -+ /* Unregister the device */ -+ ret = unregister_chrdev(MAJOR_NUM, CTLFILE); -+ -+ /* If there's an error, report it */ -+ if (ret < 0) -+ DEBUG(DBG_ERROR, "errorcode: %d\n", ret); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.c linux-2.4.25/net/ipv6/mobile_ip6/mdetect.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mdetect.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,1153 @@ -+/* -+ * Movement Detection Module -+ * -+ * Authors: -+ * Henrik Petander <lpetande@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Handles the L3 movement detection of mobile node and also -+ * changing of its routes. -+ * -+ */ -+ -+/* -+ * Changes: -+ * -+ * Nanno Langstraat : Locking fixes -+ * Venkata Jagana : Locking fix -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/if_arp.h> -+#include <linux/route.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipglue.h> -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include "util.h" -+#include "mdetect.h" -+#include "mn.h" -+#include "debug.h" -+#include "multiaccess_ctl.h" -+ -+#define START 0 -+#define CONTINUE 1 -+#define OK 2 -+#define DEBUG_MDETECT 7 -+ -+#define DEF_RTR_POLL_IVAL 5 /* In seconds */ -+ -+#define NO_RTR 0 -+#define RTR_SUSPECT 1 -+#define CURR_RTR_OK 2 -+ -+#define RA_RCVD 0 -+#define NA_RCVD 1 -+#define TIMEOUT 2 -+ -+#define MIPV6_MDF_NONE 0x0 -+#define MIPV6_MDF_HAS_RTR_PREV 0x1 -+ -+#define ROUTER_REACHABLE 1 -+#define RADV_MISSED 2 -+#define NOT_REACHABLE 3 -+ -+/* R_TIME_OUT paramater is used to make the decision when to change the -+ * default router, if the current one is unreachable. 2s is pretty aggressive -+ * and may result in hopping between two routers. OTOH a small value enhances -+ * the performance -+ */ -+#define R_TIME_OUT 30*HZ -+ -+/* maximum RA interval for router unreachability detection */ -+#define MAX_RADV_INTERVAL 6*HZ /* 6000 ms... */ -+ -+/* Threshold for exponential resending of router solicitations */ -+#define RS_RESEND_LINEAR 10*HZ -+ -+#define EAGER_CELL_SWITCHING 1 -+#define LAZY_CELL_SWITCHING 0 -+#define RESPECT_DAD 1 -+ -+#define ROUTER_ADDRESS 0x20 -+ -+/* RA flags */ -+#define ND_RA_FLAG_MANAGED 0x80 -+#define ND_RA_FLAG_OTHER 0x40 -+#define ND_RA_FLAG_HA 0x20 -+ -+/* DAD flags for global and link local addresses */ -+ -+#define COA_TENTATIVE 0x10 -+#define LLADDR_TENTATIVE 0x01 -+ -+struct router { -+ struct list_head list; -+ struct in6_addr ll_addr; -+ struct in6_addr raddr; /* Also contains prefix */ -+ __u8 link_addr[MAX_ADDR_LEN]; /* link layer address */ -+ __u8 link_addr_len; -+ __u8 state; -+ __u8 is_current; -+ __u8 reachable; -+ int ifindex; -+ int pfix_len; /* Length of the network prefix */ -+ unsigned long lifetime; /* from ra */ -+ __u32 last_ns_sent; -+ __u32 last_ra_rcvd; -+ __u32 interval; /* ra interval in milliseconds, 0 if not set */ -+ int glob_addr; /*Whether raddr contains also routers global address*/ -+ __u8 flags; /* RA flags, for example ha */ -+ struct in6_addr CoA; /* care-off address used with this router */ -+ int extra_addr_route; -+}; -+ -+/* dad could also be RESPECT_DAD for duplicate address detection of -+ new care-of addresses */ -+static int dad = 0; -+ -+/* Only one choice, nothing else implemented */ -+int max_rtr_reach_time = DEF_RTR_POLL_IVAL; -+ -+ -+int eager_cell_switching = EAGER_CELL_SWITCHING; /* Can be set to 0 via proc */ -+static spinlock_t router_lock; -+static spinlock_t ho_lock; -+ -+static void coa_timer_handler(unsigned long arg); -+static void timer_handler(unsigned long foo); -+static struct router *curr_router = NULL, *next_router = NULL; -+static struct timer_list r_timer = { function: timer_handler }; -+static struct timer_list coa_timer = { function: coa_timer_handler }; -+#define MAX_ROUTERS 1000 -+static LIST_HEAD(rtr_list); -+static int num_routers = 0; -+static struct handoff *_ho = NULL; -+/* -+ * Functions for handling the default router list, which movement -+ * detection uses for avoiding loops etc. -+ */ -+ -+/* TODO: Send NS to router after MAX interval has passed from last RA */ -+static int mipv6_router_state(struct router *rtr) { -+ if (rtr->interval) { -+ if (time_before(jiffies, (rtr->last_ra_rcvd + (rtr->interval * HZ) / 1000))) -+ return ROUTER_REACHABLE; -+ else -+ return NOT_REACHABLE; -+ } -+ else -+ if (time_after(jiffies, rtr->last_ra_rcvd + (rtr->lifetime * HZ))) -+ return NOT_REACHABLE; -+ return ROUTER_REACHABLE; -+} -+ -+/* searches for a specific router or any router that is reachable, -+ * if address is NULL. Also deletes obsolete routers. -+ */ -+static void mipv6_router_gc(void) -+{ -+ struct router *curr = NULL; -+ struct list_head *lh, *lh_tmp; -+ -+ DEBUG_FUNC(); -+ -+ list_for_each_safe(lh, lh_tmp, &rtr_list) { -+ curr = list_entry(lh, struct router, list); -+ if (mipv6_router_state(curr) == NOT_REACHABLE && !curr->is_current) { -+ num_routers--; -+ list_del_init(&curr->list); -+ DEBUG(DBG_DATADUMP, "Deleting unreachable router %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&curr->raddr)); -+ kfree(curr); -+ } -+ else { -+ DEBUG(DBG_DATADUMP, "NOT Deleting router %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&curr->raddr)); -+ } -+ } -+} -+ -+static struct router *mipv6_rtr_get(struct in6_addr *search_addr) -+{ -+ struct router *rtr = NULL; -+ struct list_head *lh; -+ -+ DEBUG_FUNC(); -+ -+ if (search_addr == NULL) -+ return NULL; -+ list_for_each(lh, &rtr_list) { -+ rtr = list_entry(lh, struct router, list); -+ if(!ipv6_addr_cmp(search_addr, &rtr->raddr)) { -+ return rtr; -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * Adds router to list -+ */ -+static struct router *mipv6_rtr_add(struct router *nrt) -+{ -+ -+ struct router *rptr; -+ -+ DEBUG_FUNC(); -+ -+ /* check if someone is trying DoS attack, or we just have some -+ memory leaks... */ -+ if (num_routers > MAX_ROUTERS) { -+ DEBUG(DBG_CRITICAL, -+ "failed to add new router, MAX_ROUTERS exceeded"); -+ return NULL; -+ } -+ -+ rptr = kmalloc(sizeof(struct router), GFP_ATOMIC); -+ if (rptr) { -+ memcpy(rptr, nrt, sizeof(struct router)); -+ list_add(&rptr->list, &rtr_list); -+ num_routers++; -+ } -+ DEBUG(DBG_INFO, "Adding router: %x:%x:%x:%x:%x:%x:%x:%x, " -+ "lifetime : %d sec, adv.interval: %d millisec", -+ NIPV6ADDR(&rptr->raddr), rptr->lifetime, rptr->interval); -+ -+ DEBUG(DBG_INFO, "num_routers after addition: %d", num_routers); -+ return rptr; -+} -+ -+/* Cleans up the list */ -+static void list_free(struct router **curr_router_p) -+{ -+ struct router *tmp; -+ struct list_head *lh, *lh_tmp; -+ -+ DEBUG_FUNC(); -+ -+ DEBUG(DBG_INFO, "Freeing the router list"); -+ /* set curr_router->prev_router and curr_router NULL */ -+ *curr_router_p = NULL; -+ list_for_each_safe(lh, lh_tmp, &rtr_list) { -+ tmp = list_entry(lh, struct router, list); -+ DEBUG(DBG_INFO, "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&tmp->ll_addr)); -+ list_del(&tmp->list); -+ kfree(tmp); -+ num_routers--; -+ } -+} -+ -+int rs_state = START; -+ -+/* Sends router solicitations to all valid devices -+ * source = link local address (of sending interface) -+ * dstaddr = all routers multicast address -+ * Solicitations are sent at an exponentially decreasing rate -+ * -+ * TODO: send solicitation first at a normal rate (from ipv6) and -+ * after that use the exponentially increasing intervals -+ */ -+static int rs_send(void) -+{ -+ struct net_device *dev; -+ struct in6_addr raddr, lladdr; -+ struct inet6_dev *in6_dev = NULL; -+ static int num_rs; -+ -+ if (rs_state == START) { -+ num_rs = 0; -+ rs_state = CONTINUE; -+ } else if (num_rs++ > MAX_RTR_SOLICITATIONS) -+ return HZ; -+ -+ ipv6_addr_all_routers(&raddr); -+ read_lock(&dev_base_lock); -+ -+ /* Send router solicitations to all interfaces */ -+ for (dev = dev_base; dev; dev = dev->next) { -+ if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER) { -+ DEBUG(DBG_DATADUMP, "Sending RS to device %s", -+ dev->name); -+ if (!ipv6_get_lladdr(dev, &lladdr)) { -+ ndisc_send_rs(dev, &lladdr, &raddr); -+ in6_dev = in6_dev_get(dev); -+ in6_dev->if_flags |= IF_RS_SENT; -+ in6_dev_put(in6_dev); -+ } else { -+ DEBUG(DBG_DATADUMP, "%s: device doesn't have link-local address!\n", dev->name); -+ continue; -+ } -+ } -+ -+ } -+ read_unlock(&dev_base_lock); -+ return RTR_SOLICITATION_INTERVAL; -+} -+ -+/* Create a new CoA for MN and also add a route to it if it is still tentative -+ to allow MN to get packets to the address immediately -+ */ -+static int form_coa(struct in6_addr *coa, struct in6_addr *pfix, -+ int plen, int ifindex) -+{ -+ struct net_device *dev; -+ struct inet6_dev *in6_dev; -+ int ret = 0; -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) { -+ DEBUG(DBG_WARNING, "Device is not present"); -+ return -1; -+ } -+ if ((in6_dev = in6_dev_get(dev)) == NULL) { -+ DEBUG(DBG_WARNING, "inet6_dev is not present"); -+ dev_put(dev); -+ return -1; -+ } -+ coa->s6_addr32[0] = pfix->s6_addr32[0]; -+ coa->s6_addr32[1] = pfix->s6_addr32[1]; -+ -+ if (ipv6_generate_eui64(coa->s6_addr + 8, dev) && -+ ipv6_inherit_eui64(coa->s6_addr + 8, in6_dev)) { -+ in6_dev_put(in6_dev); -+ dev_put(dev); -+ return -1; -+ } -+ if (ipv6_chk_addr(coa, dev) == 0) { -+ DEBUG(DBG_WARNING, "care-of address still tentative"); -+ ret = 1; -+ } -+ DEBUG(DBG_INFO, "Formed new CoA: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(coa)); -+ -+ in6_dev_put(in6_dev); -+ dev_put(dev); -+ return ret; -+} -+ -+static inline int rtr_is_gw(struct router *rtr, struct rt6_info *rt) -+{ -+ return ((rt->rt6i_flags & RTF_GATEWAY) && -+ !ipv6_addr_cmp(&rt->rt6i_gateway, &rtr->ll_addr)); -+} -+ -+static inline int is_prefix_route(struct router *rtr, struct rt6_info *rt) -+{ -+ return (!(rt->rt6i_flags & RTF_GATEWAY) && -+ mipv6_prefix_compare(&rt->rt6i_dst.addr, &rtr->raddr, -+ rtr->pfix_len)); -+} -+ -+/* -+ * Function that determines whether given rt6_info should be destroyed -+ * (negative => destroy rt6_info, zero or positive => do nothing) -+ */ -+static int mn_route_cleaner(struct rt6_info *rt, void *arg) -+{ -+ int type; -+ -+ struct router *rtr = (struct router *)arg; -+ -+ int ret = -1; -+ -+ DEBUG_FUNC(); -+ -+ if (!rt || !rtr) { -+ DEBUG(DBG_ERROR, "mn_route_cleaner: rt or rtr NULL"); -+ return 0; -+ } -+ -+ /* Do not delete routes to local addresses or to multicast -+ * addresses, since we need them to get router advertisements -+ * etc. Multicast addresses are more tricky, but we don't -+ * delete them in any case. The routing mechanism is not optimal for -+ * multihoming. -+ * -+ * Also keep all new prefix routes, gateway routes through rtr and -+ * all remaining default routes (including those used for reverse -+ * tunneling) -+ */ -+ type = ipv6_addr_type(&rt->rt6i_dst.addr); -+ -+ if ((type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) || -+ rt->rt6i_dev == &loopback_dev || rtr_is_gw(rtr, rt) || -+ is_prefix_route(rtr, rt) || (rt->rt6i_flags & RTF_DEFAULT)) -+ ret = 0; -+ -+ /* delete all others */ -+ -+ if (rt->rt6i_dev != &loopback_dev) { -+ DEBUG(DEBUG_MDETECT, -+ "%s route:\n" -+ "dev: %s,\n" -+ "gw: %x:%x:%x:%x:%x:%x:%x:%x,\n" -+ "flags: %x,\n" -+ "metric: %d,\n" -+ "src: %x:%x:%x:%x:%x:%x:%x:%x,\n" -+ "dst: %x:%x:%x:%x:%x:%x:%x:%x,\n" -+ "plen: %d\n", -+ (ret ? "Deleting" : "Keeping"), -+ rt->rt6i_dev->name, -+ NIPV6ADDR(&rt->rt6i_gateway), -+ rt->rt6i_flags, -+ rt->rt6i_metric, -+ NIPV6ADDR(&rt->rt6i_src.addr), -+ NIPV6ADDR(&rt->rt6i_dst.addr), -+ rt->rt6i_dst.plen); -+ } -+ return ret; -+} -+ -+/* -+ * Deletes old routes -+ */ -+static __inline__ void delete_routes(struct router *rtr) -+{ -+ DEBUG_FUNC(); -+ -+ /* Routing table is locked to ensure that nobody uses its */ -+ write_lock_bh(&rt6_lock); -+ DEBUG(DBG_INFO, "mipv6: Purging routes"); -+ /* TODO: Does not prune, should it? */ -+ fib6_clean_tree(&ip6_routing_table, -+ mn_route_cleaner, 0, rtr); -+ write_unlock_bh(&rt6_lock); -+ -+} -+ -+ -+static __inline__ void delete_coas(struct router *rtr) -+{ -+ struct net_device *dev; -+ struct inet6_dev *idev; -+ struct inet6_ifaddr *ifa; -+ -+ dev = dev_get_by_index(rtr->ifindex); -+ if (!dev) -+ return; -+ -+ idev = in6_dev_get(dev); -+ -+ if (idev) { -+ read_lock_bh(&idev->lock); -+ ifa = idev->addr_list; -+ while (ifa) { -+ int keep; -+ spin_lock(&ifa->lock); -+ -+ keep = (ifa->flags&(IFA_F_PERMANENT|IFA_F_HOMEADDR) || -+ !ipv6_addr_cmp(&ifa->addr, &rtr->CoA)); -+ -+ spin_unlock(&ifa->lock); -+ -+ if (keep) -+ ifa = ifa->if_next; -+ else { -+ in6_ifa_hold(ifa); -+ read_unlock_bh(&idev->lock); -+ -+ ipv6_del_addr(ifa); -+ -+ read_lock_bh(&idev->lock); -+ ifa = idev->addr_list; -+ } -+ } -+ read_unlock_bh(&idev->lock); -+ in6_dev_put(idev); -+ } -+ dev_put(dev); -+} -+ -+int next_mdet_state[3][3] = {{CURR_RTR_OK, NO_RTR, NO_RTR}, -+ {CURR_RTR_OK, CURR_RTR_OK, NO_RTR}, -+ {CURR_RTR_OK, CURR_RTR_OK, RTR_SUSPECT}}; -+ -+char *states[3] = {"NO_RTR", "RTR_SUSPECT", "CURR_RTR_OK"}; -+char *events[3] = {"RA_RCVD", "NA_RCVD", "TIMEOUT"}; -+ -+/* State transitions -+ * NO_RTR, RA_RCVD -> CURR_RTR_OK -+ * NO_RTR, NA_RCVD -> NO_RTR -+ * NO_RTR, TIMEOUT -> NO_RTR -+ -+ * RTR_SUSPECT, RA_RCVD -> CURR_RTR_OK -+ * RTR_SUSPECT, NA_RCVD -> CURR_RTR_OK -+ * RTR_SUSPECT, TIMEOUT -> NO_RTR -+ -+ * CURR_RTR_OK, RA_RCVD -> CURR_RTR_OK -+ * CURR_RTR_OK, NA_RCVD -> CURR_RTR_OK -+ * CURR_RTR_OK, TIMEOUT -> RTR_SUSPECT -+ */ -+static int _curr_state = NO_RTR; -+ -+#if 0 -+static int get_mdet_state(void){ -+ int state; -+ spin_lock_bh(&router_lock); -+ state = _curr_state; -+ spin_unlock_bh(&router_lock); -+ return state; -+} -+#endif -+ -+/* Needs to be called with router_lock locked */ -+static int mdet_statemachine(int event) -+{ -+ -+ if (event > 2 || _curr_state > 2) { -+ DEBUG(DBG_ERROR, "Got illegal event or curr_state"); -+ return -1; -+ } -+ -+ DEBUG(DBG_DATADUMP, "Got event %s and curr_state is %s", -+ events[event], states[_curr_state]); -+ -+ _curr_state = next_mdet_state[_curr_state][event]; -+ DEBUG(DBG_DATADUMP, "Next state is %s", states[_curr_state]); -+ return _curr_state; -+} -+ -+static void mipv6_do_ll_dad(int ifindex) -+{ -+ struct net_device *dev = dev_get_by_index(ifindex); -+ if (dev) { -+ struct in6_addr lladdr; -+ struct inet6_ifaddr *ifa; -+ if (!ipv6_get_lladdr(dev, &lladdr) && -+ (ifa = ipv6_get_ifaddr(&lladdr, dev)) != NULL) { -+ spin_lock_bh(&ifa->lock); -+ if (!(ifa->flags & IFA_F_TENTATIVE)) { -+ ifa->flags |= IFA_F_TENTATIVE; -+ spin_unlock_bh(&ifa->lock); -+ addrconf_dad_start(ifa, 0); -+ } else -+ spin_unlock_bh(&ifa->lock); -+ -+ } -+ dev_put(dev); -+ } -+} -+/* -+ * Changes the router, called from ndisc.c if mipv6_router_event -+ * returns true. -+ */ -+ -+static void mipv6_change_router(void) -+{ -+ struct in6_addr coa; -+ int ret, ifindex; -+ -+ DEBUG_FUNC(); -+ -+ -+ if (next_router == NULL) -+ return; -+ -+ spin_lock(&router_lock); -+ -+ -+ if (curr_router != NULL && -+ !ipv6_addr_cmp(&curr_router->ll_addr, &next_router->ll_addr)) { -+ DEBUG(DBG_INFO,"Trying to handoff from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&curr_router->ll_addr)); -+ DEBUG(DBG_INFO,"Trying to handoff to: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&next_router->ll_addr)); -+ next_router = NULL; /* Let's not leave dangling pointers */ -+ spin_unlock(&router_lock); -+ return; -+ } -+ ret = form_coa(&next_router->CoA, &next_router->raddr, -+ next_router->pfix_len, next_router->ifindex); -+ if (ret < 0) { -+ DEBUG(DBG_ERROR, "handoff: Creation of coa failed"); -+ spin_unlock(&router_lock); -+ return; -+ } else if (ret > 0) -+ next_router->flags |= COA_TENTATIVE; -+ -+ mdet_statemachine(RA_RCVD); /* TODO: What if DAD fails... */ -+ if (next_router->interval) -+ mod_timer(&r_timer, jiffies + -+ (next_router->interval * HZ)/1000); -+ else -+ mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ); -+ -+ -+ if (ret == 0) { -+ ipv6_addr_copy(&coa, &next_router->CoA); -+ ifindex = next_router->ifindex; -+ spin_unlock(&router_lock); -+ mipv6_mdet_finalize_ho(&coa, ifindex); -+ return; -+ } -+ spin_unlock(&router_lock); -+ -+} -+static unsigned long ns_send(void) -+{ -+ struct neighbour *neigh; -+ struct net_device *dev; -+ struct in6_addr *raddr; -+ -+ DEBUG(DBG_DATADUMP, "Sending Neighbour solicitation to default router to verify its reachability"); -+ if (!curr_router) -+ return HZ; -+ if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL) -+ return HZ; -+ if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) { -+ dev_put(dev); -+ return HZ; -+ } -+ if (curr_router->glob_addr) -+ raddr = &curr_router->raddr; -+ else -+ raddr = &curr_router->ll_addr; -+ -+ curr_router->last_ns_sent = jiffies; -+ ndisc_send_ns(dev, neigh, raddr, raddr, NULL); -+ -+ neigh_release(neigh); -+ dev_put(dev); -+ return HZ/5; /* Wait 200ms for a reply */ -+} -+ -+static int na_rcvd(void) -+{ -+ int neigh_ok = 0; -+ struct neighbour *neigh; -+ struct net_device *dev; -+ -+ if (!curr_router) -+ return 0; -+ if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL) -+ return 0; -+ if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) { -+ dev_put(dev); -+ return 0; -+ } -+ if (neigh->flags & NTF_ROUTER && -+ (time_after(neigh->confirmed, curr_router->last_ns_sent) || -+ neigh->confirmed == curr_router->last_ns_sent)) { -+ neigh_ok = 1; -+ DEBUG(DBG_DATADUMP, "Mdetect event: NA rcvd from curr rtr"); -+ } else -+ DEBUG(DBG_DATADUMP, "Mdetect event: NA NOT rcvd from curr rtr within time limit"); -+ neigh_release(neigh); -+ dev_put(dev); -+ return neigh_ok; -+} -+ -+static void coa_timer_handler(unsigned long dummy) -+{ -+ -+ spin_lock_bh(&ho_lock); -+ if (_ho) { -+ DEBUG(DBG_INFO, "Starting handoff after DAD"); -+ mipv6_mobile_node_moved(_ho); -+ kfree(_ho); -+ _ho = NULL; -+ } -+ spin_unlock_bh(&ho_lock); -+} -+static void timer_handler(unsigned long foo) -+{ -+ unsigned long timeout; -+ int state; -+ spin_lock_bh(&router_lock); -+ -+ if (_curr_state != NO_RTR) -+ rs_state = START; -+ -+ if (_curr_state == RTR_SUSPECT && na_rcvd()) { -+ state = mdet_statemachine(NA_RCVD); -+ timeout = curr_router->interval ? curr_router->interval : max_rtr_reach_time * HZ; -+ } else { -+ state = mdet_statemachine(TIMEOUT); -+ if (state == NO_RTR) -+ timeout = rs_send(); -+ else /* RTR_SUSPECT */ -+ timeout = ns_send(); -+ } -+ if (!timeout) -+ timeout = HZ; -+ -+ mipv6_router_gc(); -+ mod_timer(&r_timer, jiffies + timeout); -+ spin_unlock_bh(&router_lock); -+} -+ -+/** -+ * mipv6_get_care_of_address - get node's care-of primary address -+ * @homeaddr: one of node's home addresses -+ * @coaddr: buffer to store care-of address -+ * -+ * Stores the current care-of address in the @coaddr, assumes -+ * addresses in EUI-64 format. Since node might have several home -+ * addresses caller MUST supply @homeaddr. If node is at home -+ * @homeaddr is stored in @coaddr. Returns 0 on success, otherwise a -+ * negative value. -+ **/ -+int mipv6_get_care_of_address( -+ struct in6_addr *homeaddr, struct in6_addr *coaddr) -+{ -+ -+ DEBUG_FUNC(); -+ -+ if (homeaddr == NULL) -+ return -1; -+ spin_lock_bh(&router_lock); -+ if (curr_router == NULL || mipv6_mn_is_at_home(homeaddr) || -+ mipv6_prefix_compare(homeaddr, &curr_router->raddr, 64) || -+ curr_router->flags&COA_TENTATIVE) { -+ DEBUG(DBG_INFO, -+ "mipv6_get_care_of_address: returning home address"); -+ ipv6_addr_copy(coaddr, homeaddr); -+ spin_unlock_bh(&router_lock); -+ return 0; -+ -+ } -+ -+ /* At home or address check failure probably due to dad wait */ -+ if (mipv6_prefix_compare(&curr_router->raddr, homeaddr, -+ curr_router->pfix_len) -+ || (dad == RESPECT_DAD && -+ (ipv6_chk_addr(coaddr, NULL) == 0))) { -+ ipv6_addr_copy(coaddr, homeaddr); -+ } else { -+ ipv6_addr_copy(coaddr, &curr_router->CoA); -+ } -+ -+ spin_unlock_bh(&router_lock); -+ return 0; -+} -+ -+int mipv6_mdet_del_if(int ifindex) -+{ -+ struct router *curr = NULL; -+ struct list_head *lh, *lh_tmp; -+ -+ spin_lock_bh(&router_lock); -+ list_for_each_safe(lh, lh_tmp, &rtr_list) { -+ curr = list_entry(lh, struct router, list); -+ if (curr->ifindex == ifindex) { -+ num_routers--; -+ list_del_init(&curr->list); -+ DEBUG(DBG_DATADUMP, "Deleting router %x:%x:%x:%x:%x:%x:%x:%x on interface %d", -+ NIPV6ADDR(&curr->raddr), ifindex); -+ if (curr_router == curr) -+ curr_router = NULL; -+ kfree(curr); -+ } -+ } -+ spin_unlock_bh(&router_lock); -+ return 0; -+} -+ -+void mipv6_mdet_retrigger_ho(void) -+{ -+ struct handoff ho; -+ -+ spin_lock_bh(&router_lock); -+ if (curr_router != NULL) { -+ ho.coa = &curr_router->CoA; -+ ho.plen = curr_router->pfix_len; -+ ho.ifindex = curr_router->ifindex; -+ ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr); -+ ho.home_address = (curr_router->glob_addr && -+ curr_router->flags&ND_RA_FLAG_HA); -+ } -+ spin_unlock_bh(&router_lock); -+ mipv6_mobile_node_moved(&ho); -+} -+ -+void mipv6_mdet_set_curr_rtr_reachable(int reachable) -+{ -+ spin_lock_bh(&router_lock); -+ if (curr_router != NULL) { -+ curr_router->reachable = reachable; -+ } -+ spin_unlock_bh(&router_lock); -+ -+} -+ -+int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex) -+{ -+ int dummy; -+ struct handoff ho; -+ struct router *tmp; -+ struct net_device *dev; -+ struct in6_addr ll_addr; -+ -+ spin_lock_bh(&router_lock); -+ -+ if (!next_router) { -+ spin_unlock_bh(&router_lock); -+ return 0; -+ } -+ -+ dev = dev_get_by_index(next_router->ifindex); -+ -+ if (ipv6_get_lladdr(dev, &ll_addr) == 0) { -+ if (ipv6_addr_cmp(&ll_addr, coa) == 0) -+ DEBUG(DBG_INFO, "DAD for link local address completed"); -+ next_router->flags &= ~LLADDR_TENTATIVE; -+ } -+ -+ dev_put(dev); -+ -+ if (mipv6_prefix_compare(coa, &next_router->CoA, -+ next_router->pfix_len)) { -+ DEBUG(DBG_INFO, "DAD for Care-of address completed"); -+ next_router->flags &= ~COA_TENTATIVE; -+ } -+ if (!(next_router->flags&LLADDR_TENTATIVE) && !(next_router->flags&COA_TENTATIVE)) { -+ DEBUG(DBG_INFO, "%s: Proceeding with handoff after DAD\n", __FUNCTION__); -+ tmp = curr_router; -+ curr_router = next_router; -+ curr_router->is_current = 1; -+ next_router = NULL; -+ curr_router->flags &= ~COA_TENTATIVE; -+ delete_routes(curr_router); -+ delete_coas(curr_router); -+ if (tmp) { -+ struct net_device *dev_old = dev_get_by_index(tmp->ifindex); -+ struct rt6_info *rt = NULL; -+ if (dev_old) { -+ rt = rt6_get_dflt_router(&tmp->ll_addr, dev_old); -+ dev_put(dev_old); -+ } -+ if (rt) -+ ip6_del_rt(rt, NULL); -+ tmp->is_current = 0; -+ } -+ -+ ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy); -+ ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy); -+ -+ -+ ho.coa = &curr_router->CoA; -+ ho.plen = curr_router->pfix_len; -+ ho.ifindex = curr_router->ifindex; -+ ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr); -+ ho.home_address = (curr_router->glob_addr && -+ curr_router->flags&ND_RA_FLAG_HA); -+ -+ spin_unlock_bh(&router_lock); -+ mipv6_mobile_node_moved(&ho); -+ } else -+ spin_unlock_bh(&router_lock); -+ return 0; -+} -+/* Decides whether router candidate is the same router as current rtr -+ * based on prefix / global addresses of the routers and their link local -+ * addresses -+ */ -+static int is_current_rtr(struct router *nrt, struct router *crt) -+{ -+ DEBUG_FUNC(); -+ -+ DEBUG(DEBUG_MDETECT, "Current router: " -+ "%x:%x:%x:%x:%x:%x:%x:%x and", NIPV6ADDR(&crt->raddr)); -+ DEBUG(DEBUG_MDETECT, "Candidate router: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&nrt->raddr)); -+ -+ return (!ipv6_addr_cmp(&nrt->raddr,&crt->raddr) && -+ !ipv6_addr_cmp(&nrt->ll_addr, &crt->ll_addr)); -+} -+ -+/* -+ * Change next router to nrtr -+ * Returns 1, if router has been changed. -+ */ -+ -+static int change_next_rtr(struct router *nrtr, struct router *ortr) -+{ -+ int changed = 0; -+ DEBUG_FUNC(); -+ -+ if (!next_router || ipv6_addr_cmp(&nrtr->raddr, &next_router->raddr)) { -+ changed = 1; -+ } -+ next_router = nrtr; -+ return changed; -+} -+static int clean_ncache(struct router *nrt, struct router *ort, int same_if) -+{ -+ struct net_device *ortdev; -+ DEBUG_FUNC(); -+ -+ /* Always call ifdown after a handoff to ensure proper routing */ -+ -+ if (!ort) -+ return 0; -+ if ((ortdev = dev_get_by_index(ort->ifindex)) == NULL) { -+ DEBUG(DBG_WARNING, "Device is not present"); -+ return -1; -+ } -+ neigh_ifdown(&nd_tbl, ortdev); -+ dev_put(ortdev); -+ return 0; -+} -+ -+static int mdet_get_if_preference(int ifi) -+{ -+ int pref = 0; -+ -+ DEBUG_FUNC(); -+ -+ pref = ma_ctl_get_preference(ifi); -+ -+ DEBUG(DEBUG_MDETECT, "ifi: %d preference %d", ifi, pref); -+ -+ return pref; -+} -+ -+/* -+ * Called from mipv6_mn_ra_rcv to determine whether to do a handoff. -+ */ -+static int mipv6_router_event(struct router *rptr) -+{ -+ struct router *nrt = NULL; -+ int new_router = 0, same_if = 1; -+ int oldstate = _curr_state; -+ int addrtype = ipv6_addr_type(&rptr->raddr); -+ -+ DEBUG_FUNC(); -+ -+ if (rptr->lifetime == 0) -+ return MIPV6_IGN_RTR; -+ DEBUG(DEBUG_MDETECT, "Received a RA from router: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&rptr->raddr)); -+ spin_lock(&router_lock); -+ -+ /* Add or update router entry */ -+ if ((nrt = mipv6_rtr_get(&rptr->raddr)) == NULL) { -+ if (addrtype == IPV6_ADDR_ANY || (nrt = mipv6_rtr_add(rptr)) == NULL) { -+ spin_unlock(&router_lock); -+ return MIPV6_IGN_RTR; -+ } -+ DEBUG(DBG_INFO, "Router not on list,adding it to the list"); -+ new_router = 1; -+ } -+ nrt->last_ra_rcvd = jiffies; -+ nrt->state = ROUTER_REACHABLE; -+ nrt->interval = rptr->interval; -+ nrt->lifetime = rptr->lifetime; -+ nrt->ifindex = rptr->ifindex; -+ nrt->flags = rptr->flags; -+ nrt->glob_addr = rptr->glob_addr; -+ -+ /* Whether from current router */ -+ if (curr_router && curr_router->reachable && -+ is_current_rtr(nrt, curr_router)) { -+ if (nrt->interval) -+ mod_timer(&r_timer, jiffies + (nrt->interval * HZ)/1000); -+ else -+ mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ); -+ mdet_statemachine(RA_RCVD); -+ spin_unlock(&router_lock); -+ return MIPV6_ADD_RTR; -+ } else if (oldstate == NO_RTR) { -+ rt6_purge_dflt_routers(0); /* For multiple interface case */ -+ DEBUG(DBG_INFO, "No router or router not reachable, switching to new one"); -+ goto handoff; -+ } -+ if (!curr_router) { -+ /* Startup */ -+ goto handoff; -+ } -+ /* Router behind same interface as current one ?*/ -+ same_if = (nrt->ifindex == curr_router->ifindex); -+ /* Switch to new router behind same interface if eager cell -+ * switching is used or if the interface is preferred -+ */ -+ if ((new_router && eager_cell_switching && same_if) || -+ (mdet_get_if_preference(nrt->ifindex) > -+ mdet_get_if_preference(curr_router->ifindex))) { -+ DEBUG(DBG_INFO, "Switching to new router."); -+ goto handoff; -+ } -+ -+ /* No handoff, don't add default route */ -+ DEBUG(DEBUG_MDETECT, "Ignoring RA"); -+ spin_unlock(&router_lock); -+ return MIPV6_IGN_RTR; -+handoff: -+ clean_ncache(nrt, curr_router, same_if); -+ nrt->reachable = 1; -+ if (same_if && change_next_rtr(nrt, curr_router)) { -+ mipv6_do_ll_dad(nrt->ifindex); -+ nrt->flags |= LLADDR_TENTATIVE; -+ } -+ spin_unlock(&router_lock); -+ -+ return MIPV6_CHG_RTR; -+} -+ -+/* -+ * Called from ndisc.c's router_discovery. -+ */ -+ -+static inline int ret_to_ha(struct in6_addr *addr) -+{ -+ int res = 0; -+ struct mn_info *minfo; -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_ha(addr); -+ if (minfo != NULL) { -+ spin_lock(&minfo->lock); -+ if (minfo->has_home_reg) { -+ res = 1; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock(&mn_info_lock); -+ return res; -+} -+ -+static int mipv6_mn_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts) -+{ -+ int ifi = ((struct inet6_skb_parm *)skb->cb)->iif; -+ struct ra_msg *ra = (struct ra_msg *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct router nrt; -+ struct in6_addr *ha = NULL; -+ u8 *lladdr = NULL; -+ int res; -+ DEBUG_FUNC(); -+ -+ memset(&nrt, 0, sizeof(struct router)); -+ -+ if (ra->icmph.icmp6_home_agent) { -+ nrt.flags |= ND_RA_FLAG_HA; -+ DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_HA up"); -+ } -+ -+ if (ra->icmph.icmp6_addrconf_managed) { -+ nrt.flags |= ND_RA_FLAG_MANAGED; -+ DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_MANAGED up"); -+ } -+ -+ if (ra->icmph.icmp6_addrconf_other) { -+ nrt.flags |= ND_RA_FLAG_OTHER; -+ DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_OTHER up"); -+ } -+ -+ ipv6_addr_copy(&nrt.ll_addr, saddr); -+ nrt.ifindex = ifi; -+ nrt.lifetime = ntohs(ra->icmph.icmp6_rt_lifetime); -+ -+ if (ndopts->nd_opts_src_lladdr) { -+ lladdr = (u8 *) ndopts->nd_opts_src_lladdr+2; -+ nrt.link_addr_len = skb->dev->addr_len; -+ memcpy(nrt.link_addr, lladdr, nrt.link_addr_len); -+ } -+ if (ndopts->nd_opts_pi) { -+ struct nd_opt_hdr *p; -+ for (p = ndopts->nd_opts_pi; -+ p; -+ p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) { -+ struct prefix_info *pinfo; -+ int update = 0; -+ -+ pinfo = (struct prefix_info *) p; -+ -+ if (!pinfo->autoconf) -+ continue; -+ -+ if ((pinfo->router_address && -+ (update = ret_to_ha(&pinfo->prefix))) || -+ ipv6_addr_type(&nrt.raddr) != IPV6_ADDR_UNICAST) { -+ ipv6_addr_copy(&nrt.raddr, &pinfo->prefix); -+ nrt.pfix_len = pinfo->prefix_len; -+ if (pinfo->router_address) -+ nrt.glob_addr = 1; -+ else -+ nrt.glob_addr = 0; -+ if (update) -+ ha = &pinfo->prefix; -+ DEBUG(DBG_DATADUMP, "Address of the received " -+ "prefix info option: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&nrt.raddr)); -+ DEBUG(DBG_DATADUMP, "the length of the prefix is %d", -+ nrt.pfix_len); -+ } -+ } -+ } -+ if (ndopts->nd_opts_rai) { -+ nrt.interval = ntohl(*(__u32 *)(ndopts->nd_opts_rai+4)); -+ DEBUG(DBG_DATADUMP, -+ "received router interval option with interval : %d ", -+ nrt.interval / HZ); -+ -+ if (nrt.interval > MAX_RADV_INTERVAL) { -+ nrt.interval = 0; -+ DEBUG(DBG_DATADUMP, "but we are using: %d, " -+ "because interval>MAX_RADV_INTERVAL", -+ nrt.interval / HZ); -+ } -+ } -+ -+ res = mipv6_router_event(&nrt); -+ -+ if (ha && lladdr) { -+ mipv6_mn_ha_nd_update(__dev_get_by_index(ifi), ha, lladdr); -+ } -+ return res; -+} -+ -+int __init mipv6_initialize_mdetect(void) -+{ -+ -+ DEBUG_FUNC(); -+ -+ spin_lock_init(&router_lock); -+ spin_lock_init(&ho_lock); -+ init_timer(&coa_timer); -+ init_timer(&r_timer); -+ r_timer.expires = jiffies + HZ; -+ add_timer(&r_timer); -+ -+ /* Actual HO, also deletes old routes after the addition of new ones -+ in ndisc */ -+ MIPV6_SETCALL(mipv6_change_router, mipv6_change_router); -+ -+ MIPV6_SETCALL(mipv6_ra_rcv, mipv6_mn_ra_rcv); -+ -+ return 0; -+} -+ -+int __exit mipv6_shutdown_mdetect() -+{ -+ -+ DEBUG_FUNC(); -+ -+ MIPV6_RESETCALL(mipv6_ra_rcv); -+ MIPV6_RESETCALL(mipv6_change_router); -+ spin_lock_bh(&router_lock); -+ spin_lock(&ho_lock); -+ del_timer(&coa_timer); -+ del_timer(&r_timer); -+ /* Free the memory allocated by router list */ -+ list_free(&curr_router); -+ if (_ho) -+ kfree(_ho); -+ spin_unlock(&ho_lock); -+ spin_unlock_bh(&router_lock); -+ return 0; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.h linux-2.4.25/net/ipv6/mobile_ip6/mdetect.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mdetect.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mdetect.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,37 @@ -+/* -+ * MIPL Mobile IPv6 Movement detection module header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MDETECT_H -+#define _MDETECT_H -+ -+struct handoff { -+ int home_address; /* Is the coa a home address */ -+ int ifindex; -+ int plen; -+ struct in6_addr *coa; -+ struct in6_addr rtr_addr; /* Prefix or rtr address if coa is home address */ -+}; -+ -+int mipv6_initialize_mdetect(void); -+ -+int mipv6_shutdown_mdetect(void); -+ -+int mipv6_get_care_of_address(struct in6_addr *homeaddr, struct in6_addr *coa); -+ -+int mipv6_mdet_del_if(int ifindex); -+ -+int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex); -+ -+void mipv6_mdet_retrigger_ho(void); -+ -+void mipv6_mdet_set_curr_rtr_reachable(int reachable); -+ -+#endif /* _MDETECT_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,342 @@ -+/** -+ * Generic icmp routines -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi>, -+ * Ville Nuorvala <vnuorval@tcs.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/icmpv6.h> -+#include <net/checksum.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+#include <net/mipglue.h> -+ -+#include "debug.h" -+#include "bcache.h" -+#include "mipv6_icmp.h" -+#include "config.h" -+ -+struct mipv6_icmpv6_msg { -+ struct icmp6hdr icmph; -+ __u8 *data; -+ struct in6_addr *daddr; -+ int len; -+ __u32 csum; -+}; -+ -+#define MIPV6_ICMP_HOP_LIMIT 64 -+ -+static struct socket *mipv6_icmpv6_socket = NULL; -+static __u16 identifier = 0; -+ -+int mipv6_icmpv6_no_rcv(struct sk_buff *skb) -+{ -+ return 0; -+} -+ -+static int mipv6_icmpv6_xmit_holder = -1; -+ -+static int mipv6_icmpv6_xmit_lock_bh(void) -+{ -+ if (!spin_trylock(&mipv6_icmpv6_socket->sk->lock.slock)) { -+ if (mipv6_icmpv6_xmit_holder == smp_processor_id()) -+ return -EAGAIN; -+ spin_lock(&mipv6_icmpv6_socket->sk->lock.slock); -+ } -+ mipv6_icmpv6_xmit_holder = smp_processor_id(); -+ return 0; -+} -+ -+static __inline__ int mipv6_icmpv6_xmit_lock(void) -+{ -+ int ret; -+ local_bh_disable(); -+ ret = mipv6_icmpv6_xmit_lock_bh(); -+ if (ret) -+ local_bh_enable(); -+ return ret; -+} -+ -+static void mipv6_icmpv6_xmit_unlock_bh(void) -+{ -+ mipv6_icmpv6_xmit_holder = -1; -+ spin_unlock(&mipv6_icmpv6_socket->sk->lock.slock); -+} -+ -+static __inline__ void mipv6_icmpv6_xmit_unlock(void) -+{ -+ mipv6_icmpv6_xmit_unlock_bh(); -+ local_bh_enable(); -+} -+ -+ -+/** -+ * mipv6_icmpv6_dest_unreach - Destination Unreachable ICMP error message handler -+ * @skb: buffer containing ICMP error message -+ * -+ * Special Mobile IPv6 ICMP handling. If Correspondent Node receives -+ * persistent ICMP Destination Unreachable messages for a destination -+ * in its Binding Cache, the binding should be deleted. See draft -+ * section 8.8. -+ **/ -+static int mipv6_icmpv6_rcv_dest_unreach(struct sk_buff *skb) -+{ -+ struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; -+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) (icmph + 1); -+ int left = (skb->tail - skb->h.raw) - sizeof(*icmph)- sizeof(ipv6h); -+ struct ipv6_opt_hdr *eh; -+ struct rt2_hdr *rt2h = NULL; -+ struct in6_addr *daddr = &ipv6h->daddr; -+ struct in6_addr *saddr = &ipv6h->saddr; -+ int hdrlen, nexthdr = ipv6h->nexthdr; -+ struct mipv6_bce bce; -+ DEBUG_FUNC(); -+ -+ eh = (struct ipv6_opt_hdr *) (ipv6h + 1); -+ -+ while (left > 0) { -+ if (nexthdr != NEXTHDR_HOP && nexthdr != NEXTHDR_DEST && -+ nexthdr != NEXTHDR_ROUTING) -+ return 0; -+ -+ hdrlen = ipv6_optlen(eh); -+ if (hdrlen > left) -+ return 0; -+ -+ if (nexthdr == NEXTHDR_ROUTING) { -+ struct ipv6_rt_hdr *rth = (struct ipv6_rt_hdr *) eh; -+ -+ if (rth->type == IPV6_SRCRT_TYPE_2) { -+ if (hdrlen != sizeof(struct rt2_hdr)) -+ return 0; -+ -+ rt2h = (struct rt2_hdr *) rth; -+ -+ if (rt2h->rt_hdr.segments_left > 0) -+ daddr = &rt2h->addr; -+ break; -+ } -+ } -+ /* check for home address option in case this node is a MN */ -+ if (nexthdr == NEXTHDR_DEST) { -+ __u8 *raw = (__u8 *) eh; -+ __u16 i = 2; -+ while (1) { -+ struct mipv6_dstopt_homeaddr *hao; -+ -+ if (i + sizeof (*hao) > hdrlen) -+ break; -+ -+ hao = (struct mipv6_dstopt_homeaddr *) &raw[i]; -+ -+ if (hao->type == MIPV6_TLV_HOMEADDR && -+ hao->length == sizeof(struct in6_addr)) { -+ saddr = &hao->addr; -+ break; -+ } -+ if (hao->type) -+ i += hao->length + 2; -+ else -+ i++; -+ } -+ -+ } -+ nexthdr = eh->nexthdr; -+ eh = (struct ipv6_opt_hdr *) ((u8 *) eh + hdrlen); -+ left -= hdrlen; -+ } -+ if (rt2h == NULL) return 0; -+ -+ if (mipv6_bcache_get(daddr, saddr, &bce) == 0 && !(bce.flags&HOME_REGISTRATION)) { -+ /* A primitive algorithm for detecting persistent ICMP destination unreachable messages */ -+ if (bce.destunr_count && -+ time_after(jiffies, -+ bce.last_destunr + MIPV6_DEST_UNR_IVAL*HZ)) -+ bce.destunr_count = 0; -+ -+ bce.destunr_count++; -+ -+ mipv6_bcache_icmp_err(daddr, saddr, bce.destunr_count); -+ -+ if (bce.destunr_count > MIPV6_MAX_DESTUNREACH && mipv6_bcache_delete(daddr, saddr, CACHE_ENTRY) == 0) { -+ DEBUG(DBG_INFO, "Deleted bcache entry " -+ "%x:%x:%x:%x:%x:%x:%x:%x " -+ "%x:%x:%x:%x:%x:%x:%x:%x (reason: " -+ "%d dest unreachables) ", -+ NIPV6ADDR(daddr), NIPV6ADDR(saddr), bce.destunr_count); -+ } -+ } -+ return 0; -+} -+ -+static int mipv6_icmpv6_getfrag(const void *data, struct in6_addr *saddr, -+ char *buff, unsigned int offset, -+ unsigned int len) -+{ -+ struct mipv6_icmpv6_msg *msg = (struct mipv6_icmpv6_msg *) data; -+ struct icmp6hdr *icmph; -+ __u32 csum; -+ -+ if (offset) { -+ msg->csum = csum_partial_copy_nocheck(msg->data + offset - -+ sizeof(*icmph), buff, -+ len, msg->csum); -+ return 0; -+ } -+ -+ csum = csum_partial_copy_nocheck((__u8 *) &msg->icmph, buff, -+ sizeof(*icmph), msg->csum); -+ -+ csum = csum_partial_copy_nocheck(msg->data, buff + sizeof(*icmph), -+ len - sizeof(*icmph), csum); -+ -+ icmph = (struct icmp6hdr *) buff; -+ -+ icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len, -+ IPPROTO_ICMPV6, csum); -+ return 0; -+} -+ -+/** -+ * mipv6_icmpv6_send - generic icmpv6 message send -+ * @daddr: destination address -+ * @saddr: source address -+ * @type: icmp type -+ * @code: icmp code -+ * @id: packet identifier. If null, uses internal counter to get new id -+ * @data: packet data -+ * @datalen: length of data in bytes -+ */ -+void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr, int type, -+ int code, __u16 *id, __u16 flags, void *data, int datalen) -+{ -+ struct sock *sk = mipv6_icmpv6_socket->sk; -+ struct flowi fl; -+ struct mipv6_icmpv6_msg msg; -+ -+ DEBUG_FUNC(); -+ -+ fl.proto = IPPROTO_ICMPV6; -+ fl.fl6_dst = daddr; -+ fl.fl6_src = saddr; -+ fl.fl6_flowlabel = 0; -+ fl.uli_u.icmpt.type = type; -+ fl.uli_u.icmpt.code = code; -+ -+ msg.icmph.icmp6_type = type; -+ msg.icmph.icmp6_code = code; -+ msg.icmph.icmp6_cksum = 0; -+ -+ if (id) -+ msg.icmph.icmp6_identifier = htons(*id); -+ else -+ msg.icmph.icmp6_identifier = htons(identifier++); -+ -+ msg.icmph.icmp6_sequence = htons(flags); -+ msg.data = data; -+ msg.csum = 0; -+ msg.len = datalen + sizeof(struct icmp6hdr); -+ msg.daddr = daddr; -+ -+ if (mipv6_icmpv6_xmit_lock()) -+ return; -+ -+ ip6_build_xmit(sk, mipv6_icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1, -+ MSG_DONTWAIT); -+ -+ ICMP6_INC_STATS_BH(Icmp6OutMsgs); -+ mipv6_icmpv6_xmit_unlock(); -+} -+ -+/** -+ * icmp6_rcv - ICMPv6 receive and multiplex -+ * @skb: buffer containing ICMP message -+ * -+ * Generic ICMPv6 receive function to multiplex messages to approriate -+ * handlers. Only used for ICMP messages with special handling in -+ * Mobile IPv6. -+ **/ -+static void icmp6_rcv(struct sk_buff *skb) -+{ -+ struct icmp6hdr *hdr; -+ -+ if (skb_is_nonlinear(skb) && -+ skb_linearize(skb, GFP_ATOMIC) != 0) { -+ kfree_skb(skb); -+ return; -+ } -+ __skb_push(skb, skb->data-skb->h.raw); -+ -+ hdr = (struct icmp6hdr *) skb->h.raw; -+ -+ switch (hdr->icmp6_type) { -+ case ICMPV6_DEST_UNREACH: -+ mipv6_icmpv6_rcv_dest_unreach(skb); -+ break; -+ -+ case ICMPV6_PARAMPROB: -+ mip6_fn.icmpv6_paramprob_rcv(skb); -+ break; -+ -+ case MIPV6_DHAAD_REPLY: -+ mip6_fn.icmpv6_dhaad_rep_rcv(skb); -+ break; -+ -+ case MIPV6_PREFIX_ADV: -+ mip6_fn.icmpv6_pfxadv_rcv(skb); -+ break; -+ -+ case MIPV6_DHAAD_REQUEST: -+ mip6_fn.icmpv6_dhaad_req_rcv(skb); -+ break; -+ -+ case MIPV6_PREFIX_SOLICIT: -+ mip6_fn.icmpv6_pfxsol_rcv(skb); -+ break; -+ } -+} -+ -+int mipv6_icmpv6_init(void) -+{ -+ struct sock *sk; -+ int err; -+ -+ if ((mipv6_icmpv6_socket = sock_alloc()) == NULL) { -+ DEBUG(DBG_ERROR, "Cannot allocate mipv6_icmpv6_socket"); -+ return -1; -+ } -+ mipv6_icmpv6_socket->type = SOCK_RAW; -+ -+ if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_ICMP, -+ &mipv6_icmpv6_socket)) < 0) { -+ DEBUG(DBG_ERROR, "Cannot initialize mipv6_icmpv6_socket"); -+ sock_release(mipv6_icmpv6_socket); -+ mipv6_icmpv6_socket = NULL; /* For safety */ -+ return err; -+ } -+ sk = mipv6_icmpv6_socket->sk; -+ sk->allocation = GFP_ATOMIC; -+ sk->prot->unhash(sk); -+ -+ /* Register our ICMP handler */ -+ MIPV6_SETCALL(mipv6_icmp_rcv, icmp6_rcv); -+ return 0; -+} -+ -+void mipv6_icmpv6_exit(void) -+{ -+ MIPV6_RESETCALL(mipv6_icmp_rcv); -+ if (mipv6_icmpv6_socket) -+ sock_release(mipv6_icmpv6_socket); -+ mipv6_icmpv6_socket = NULL; /* For safety */ -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.h linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,43 @@ -+/* -+ * MIPL Mobile IPv6 ICMP send and receive prototypes -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MIPV6_ICMP -+#define _MIPV6_ICMP -+ -+#include <linux/config.h> -+#include <linux/in6.h> -+ -+void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr, -+ int type, int code, __u16 *id, __u16 flags, -+ void *data, int datalen); -+ -+void mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id); -+ -+void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr); -+/* No handling */ -+int mipv6_icmpv6_no_rcv(struct sk_buff *skb); -+ -+/* Receive DHAAD Reply message */ -+int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb); -+/* Receive Parameter Problem message */ -+int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb); -+/* Receive prefix advertisements */ -+int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb); -+ -+/* Receive DHAAD Request message */ -+int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb); -+/* Receive prefix solicitations */ -+int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb); -+ -+int mipv6_icmpv6_init(void); -+void mipv6_icmpv6_exit(void); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_ha.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,158 @@ -+/* -+ * Home Agent specific ICMP routines -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "halist.h" -+#include "debug.h" -+#include "mipv6_icmp.h" -+//#include "prefix.h" -+ -+/* Is this the easiest way of checking on -+ * which interface an anycast address is ? -+ */ -+static int find_ac_dev(struct in6_addr *addr) -+{ -+ int ifindex = 0; -+ struct net_device *dev; -+ read_lock(&dev_base_lock); -+ for (dev=dev_base; dev; dev=dev->next) { -+ if (ipv6_chk_acast_addr(dev, addr)) { -+ ifindex = dev->ifindex; -+ break; -+ } -+ } -+ read_unlock(&dev_base_lock); -+ return ifindex; -+} -+ -+/** -+ * mipv6_icmpv6_send_dhaad_rep - Reply to DHAAD Request -+ * @ifindex: index of interface request was received from -+ * @id: request's identification number -+ * @daddr: requester's IPv6 address -+ * -+ * When Home Agent receives Dynamic Home Agent Address Discovery -+ * request, it replies with a list of home agents available on the -+ * home link. -+ */ -+void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr) -+{ -+ __u8 *data = NULL; -+ struct in6_addr home, *ha_addrs = NULL; -+ int addr_count, max_addrs, size = 0; -+ -+ if (daddr == NULL) -+ return; -+ -+ if (mipv6_ha_get_addr(ifindex, &home) < 0) { -+ DEBUG(DBG_INFO, "Not Home Agent in this interface"); -+ return; -+ } -+ -+ /* We send all available HA addresses, not exceeding a maximum -+ * number we can fit in a packet with minimum IPv6 MTU (to -+ * avoid fragmentation). -+ */ -+ max_addrs = 76; -+ addr_count = mipv6_ha_get_pref_list(ifindex, &ha_addrs, max_addrs); -+ -+ if (addr_count < 0) return; -+ -+ if (addr_count != 0 && ha_addrs == NULL) { -+ DEBUG(DBG_ERROR, "addr_count = %d but return no addresses", -+ addr_count); -+ return; -+ } -+ data = (u8 *)ha_addrs; -+ -+ size = addr_count * sizeof(struct in6_addr); -+ -+ mipv6_icmpv6_send(daddr, &home, MIPV6_DHAAD_REPLY, -+ 0, &id, 0, data, size); -+ if (ha_addrs) { -+ data = NULL; -+ kfree(ha_addrs); -+ } -+} -+ -+/** -+ * mipv6_icmpv6_dhaad_req - Home Agent Address Discovery Request ICMP handler -+ * @skb: buffer containing ICMP information message -+ * -+ * Special Mobile IPv6 ICMP message. Handles Dynamic Home Agent -+ * Address Discovery Request messages. -+ **/ -+int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb) -+{ -+ struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr; -+ __u16 identifier; -+ int ifindex = 0; -+ -+ DEBUG_FUNC(); -+ -+ /* Invalid packet checks. */ -+ if (phdr->icmp6_code != 0) -+ return 0; -+ -+ identifier = ntohs(phdr->icmp6_identifier); -+ -+ /* -+ * Make sure we have the right ifindex (if the -+ * req came through another interface. -+ */ -+ ifindex = find_ac_dev(daddr); -+ if (ifindex == 0) { -+ DEBUG(DBG_WARNING, "received dhaad request to anycast address %x:%x:%x:%x:%x:%x:%x:%x" -+ " on which prefix we are not HA", -+ NIPV6ADDR(daddr)); -+ return 0; -+ } -+ -+ /* -+ * send reply with list -+ */ -+ mipv6_icmpv6_send_dhaad_rep(ifindex, identifier, saddr); -+ return 1; -+} -+#if 0 -+/** -+ * mipv6_icmpv6_handle_pfx_sol - handle prefix solicitations -+ * @skb: sk_buff including the icmp6 message -+ */ -+int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb) -+{ -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr; -+ struct inet6_ifaddr *ifp; -+ -+ DEBUG_FUNC(); -+ -+ if (!(ifp = ipv6_get_ifaddr(daddr, NULL))) -+ return -1; -+ -+ in6_ifa_put(ifp); -+ mipv6_pfx_cancel_send(saddr, -1); -+ -+ return 0; -+} -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_mn.c linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mipv6_icmp_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mipv6_icmp_mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,273 @@ -+/* -+ * Mobile Node specific ICMP routines -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/sched.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "mn.h" -+#include "bul.h" -+#include "mdetect.h" -+#include "debug.h" -+#include "mipv6_icmp.h" -+#include "util.h" -+//#include "prefix.h" -+ -+#define INFINITY 0xffffffff -+ -+/** -+ * mipv6_icmpv6_paramprob - Parameter Problem ICMP error message handler -+ * @skb: buffer containing ICMP error message -+ * -+ * Special Mobile IPv6 ICMP handling. If Mobile Node receives ICMP -+ * Parameter Problem message when using a Home Address Option, -+ * offending node should be logged and error message dropped. If -+ * error is received because of a Binding Update, offending node -+ * should be recorded in Binding Update List and no more Binding -+ * Updates should be sent to this destination. See RFC 3775 section -+ * 10.15. -+ **/ -+int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb) -+{ -+ struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *saddr = skb ? &skb->nh.ipv6h->saddr : NULL; -+ struct in6_addr *daddr = skb ? &skb->nh.ipv6h->daddr : NULL; -+ struct ipv6hdr *hdr = (struct ipv6hdr *) (phdr + 1); -+ int ulen = (skb->tail - (unsigned char *) (phdr + 1)); -+ -+ int errptr; -+ __u8 *off_octet; -+ -+ DEBUG_FUNC(); -+ -+ /* We only handle code 1 & 2 messages. */ -+ if (phdr->icmp6_code != ICMPV6_UNK_NEXTHDR && -+ phdr->icmp6_code != ICMPV6_UNK_OPTION) -+ return 0; -+ -+ /* Find offending octet in the original packet. */ -+ errptr = ntohl(phdr->icmp6_pointer); -+ -+ /* There is not enough of the original packet left to figure -+ * out what went wrong. Bail out. */ -+ if (ulen <= errptr) -+ return 0; -+ -+ off_octet = ((__u8 *) hdr + errptr); -+ DEBUG(DBG_INFO, "Parameter problem: offending octet %d [0x%2x]", -+ errptr, *off_octet); -+ -+ /* If CN did not understand Mobility Header, set BUL entry to -+ * ACK_ERROR so no further BUs are sumbitted to this CN. */ -+ if (phdr->icmp6_code == ICMPV6_UNK_NEXTHDR && -+ *off_octet == IPPROTO_MOBILITY) { -+ struct bul_inval_args args; -+ args.all_rr_states = 1; -+ args.cn = saddr; -+ args.mn = daddr; -+ write_lock(&bul_lock); -+ mipv6_bul_iterate(mn_bul_invalidate, &args); -+ write_unlock(&bul_lock); -+ } -+ -+ /* If CN did not understand Home Address Option, we log an -+ * error and discard the error message. */ -+ if (phdr->icmp6_code == ICMPV6_UNK_OPTION && -+ *off_octet == MIPV6_TLV_HOMEADDR) { -+ DEBUG(DBG_WARNING, "Correspondent node does not " -+ "implement Home Address Option receipt."); -+ return 1; -+ } -+ return 0; -+} -+ -+/** -+ * mipv6_mn_dhaad_send_req - Send DHAAD Request to home network -+ * @home_addr: address to do DHAAD for -+ * @plen: prefix length for @home_addr -+ * -+ * Send Dynamic Home Agent Address Discovery Request to the Home -+ * Agents anycast address in the nodes home network. -+ **/ -+void -+mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id) -+{ -+ struct in6_addr ha_anycast; -+ struct in6_addr careofaddr; -+ -+ if (mipv6_get_care_of_address(home_addr, &careofaddr) < 0) { -+ DEBUG(DBG_WARNING, "Could not get node's Care-of Address"); -+ return; -+ } -+ -+ if (mipv6_ha_anycast(&ha_anycast, home_addr, plen) < 0) { -+ DEBUG(DBG_WARNING, -+ "Could not get Home Agent Anycast address for home address %x:%x.%x:%x:%x:%x:%x:%x/%d", -+ NIPV6ADDR(home_addr), plen); -+ return; -+ } -+ -+ mipv6_icmpv6_send(&ha_anycast, &careofaddr, MIPV6_DHAAD_REQUEST, 0, -+ &dhaad_id, 0, NULL, 0); -+ -+} -+ -+/** -+ * mipv6_icmpv6_dhaad_rep - Home Agent Address Discovery Reply ICMP handler -+ * @skb: buffer containing ICMP information message -+ * -+ * Special Mobile IPv6 ICMP message. Handles Dynamic Home Agent -+ * Address Discovery Reply messages. -+ **/ -+int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb) -+{ -+ struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *address; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ __u16 identifier; -+ int ulen = (skb->tail - (unsigned char *) ((__u32 *) phdr + 2)); -+ int i; -+ struct in6_addr home_addr, coa; -+ struct in6_addr *first_ha = NULL; -+ struct mn_info *minfo; -+ int n_addr = ulen / sizeof(struct in6_addr); -+ -+ DEBUG_FUNC(); -+ -+ /* Invalid packet checks. */ -+ if (ulen % sizeof(struct in6_addr) != 0) -+ return 0; -+ -+ if (phdr->icmp6_code != 0) -+ return 0; -+ -+ identifier = ntohs(phdr->icmp6_identifier); -+ if (ulen > 0) { -+ address = (struct in6_addr *) ((__u32 *) phdr + 2); -+ } else { -+ address = saddr; -+ n_addr = 1; -+ } -+ -+ /* receive list of home agent addresses -+ * add to home agents list -+ */ -+ DEBUG(DBG_INFO, "DHAAD: got %d home agents", n_addr); -+ -+ first_ha = address; -+ -+ /* lookup H@ with identifier */ -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_id(identifier); -+ if (!minfo) { -+ read_unlock(&mn_info_lock); -+ DEBUG(DBG_INFO, "no mninfo with id %d", -+ identifier); -+ return 0; -+ } -+ spin_lock(&minfo->lock); -+ -+ /* Logic: -+ * 1. if old HA on list, prefer it -+ * 2. otherwise first HA on list prefered -+ */ -+ for (i = 0; i < n_addr; i++) { -+ DEBUG(DBG_INFO, "HA[%d] %x:%x:%x:%x:%x:%x:%x:%x", -+ i, NIPV6ADDR(address)); -+ if (ipv6_addr_cmp(&minfo->ha, address) == 0) { -+ spin_unlock(&minfo->lock); -+ read_unlock(&mn_info_lock); -+ return 0; -+ } -+ address++; -+ } -+ ipv6_addr_copy(&minfo->ha, first_ha); -+ spin_unlock(&minfo->lock); -+ ipv6_addr_copy(&home_addr, &minfo->home_addr); -+ read_unlock(&mn_info_lock); -+ -+ mipv6_get_care_of_address(&home_addr, &coa); -+ init_home_registration(&home_addr, &coa); -+ -+ return 1; -+} -+#if 0 -+/** -+ * mipv6_icmpv6_handle_pfx_adv - handle prefix advertisements -+ * @skb: sk_buff including the icmp6 message -+ */ -+int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb) -+{ -+ struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr; -+ __u8 *opt = (__u8 *) (hdr + 1); -+ int optlen = (skb->tail - opt); -+ unsigned long min_expire = INFINITY; -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *) skb->cb; -+ -+ DEBUG_FUNC(); -+ -+ while (optlen > 0) { -+ int len = opt[1] << 3; -+ if (len == 0) -+ goto set_timer; -+ -+ if (opt[0] == ND_OPT_PREFIX_INFO) { -+ int ifindex; -+ unsigned long expire; -+ struct prefix_info *pinfo = -+ (struct prefix_info *) opt; -+ struct net_device *dev; -+ struct mn_info *mninfo; -+ -+ read_lock(&mn_info_lock); -+ mninfo = mipv6_mninfo_get_by_ha(saddr); -+ if (mninfo == NULL) { -+ ifindex = 0; -+ } else { -+ spin_lock(&mninfo->lock); -+ ifindex = mninfo->ifindex; -+ spin_unlock(&mninfo->lock); -+ mninfo = NULL; -+ } -+ read_unlock(&mn_info_lock); -+ -+ if (!(dev = dev_get_by_index(ifindex))) { -+ DEBUG(DBG_WARNING, "Cannot find device by index %d", parm->iif); -+ goto nextopt; -+ } -+ -+ expire = ntohl(pinfo->valid); -+ expire = expire == 0 ? INFINITY : expire; -+ -+ min_expire = expire < min_expire ? expire : min_expire; -+ -+ dev_put(dev); -+ } -+ -+nextopt: -+ optlen -= len; -+ opt += len; -+ } -+ -+set_timer: -+ -+ mipv6_pfx_add_home(parm->iif, saddr, daddr, min_expire); -+ return 0; -+} -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mn.c linux-2.4.25/net/ipv6/mobile_ip6/mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,1521 @@ -+/* -+ * Mobile-node functionality -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/ipv6.h> -+#include <linux/net.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/rtnetlink.h> -+#include <linux/if_arp.h> -+#include <linux/ipsec.h> -+#include <linux/notifier.h> -+#include <linux/list.h> -+#include <linux/route.h> -+#include <linux/netfilter.h> -+#include <linux/netfilter_ipv6.h> -+#include <linux/tqueue.h> -+#include <linux/proc_fs.h> -+ -+#include <asm/uaccess.h> -+ -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/neighbour.h> -+#include <net/ndisc.h> -+#include <net/ip6_route.h> -+#include <net/mipglue.h> -+ -+#include "util.h" -+#include "mdetect.h" -+#include "bul.h" -+#include "mobhdr.h" -+#include "debug.h" -+#include "mn.h" -+#include "mipv6_icmp.h" -+#include "multiaccess_ctl.h" -+//#include "prefix.h" -+#include "tunnel_mn.h" -+#include "stats.h" -+#include "config.h" -+ -+#define MIPV6_BUL_SIZE 128 -+ -+static LIST_HEAD(mn_info_list); -+ -+/* Lock for list of MN infos */ -+rwlock_t mn_info_lock = RW_LOCK_UNLOCKED; -+ -+static spinlock_t ifrh_lock = SPIN_LOCK_UNLOCKED; -+ -+struct ifr_holder { -+ struct list_head list; -+ struct in6_ifreq ifr; -+ int old_ifi; -+ struct handoff *ho; -+}; -+ -+LIST_HEAD(ifrh_list); -+ -+static struct tq_struct mv_home_addr_task; -+ -+/* Determines whether manually configured home addresses are preferred as -+ * source addresses over dynamically configured ones -+ */ -+int mipv6_use_preconfigured_hoaddr = 1; -+ -+/* Determines whether home addresses, which are at home are preferred as -+ * source addresses over other home addresses -+ */ -+int mipv6_use_topol_corr_hoaddr = 0; -+ -+static spinlock_t icmpv6_id_lock = SPIN_LOCK_UNLOCKED; -+static __u16 icmpv6_id = 0; -+ -+static inline __u16 mipv6_get_dhaad_id(void) -+{ -+ __u16 ret; -+ spin_lock_bh(&icmpv6_id_lock); -+ ret = ++icmpv6_id; -+ spin_unlock_bh(&icmpv6_id_lock); -+ return ret; -+} -+ -+/** -+ * mipv6_mninfo_get_by_home - Returns mn_info for a home address -+ * @haddr: home address of MN -+ * -+ * Returns mn_info on success %NULL otherwise. Caller MUST hold -+ * @mn_info_lock (read or write). -+ **/ -+struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo; -+ -+ DEBUG_FUNC(); -+ -+ if (!haddr) -+ return NULL; -+ -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (!ipv6_addr_cmp(&minfo->home_addr, haddr)) { -+ spin_unlock(&minfo->lock); -+ return minfo; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ return NULL; -+} -+ -+/** -+ * mipv6_mninfo_get_by_ha - Lookup mn_info with Home Agent address -+ * @home_agent: Home Agent address -+ * -+ * Searches for a mn_info entry with @ha set to @home_agent. You MUST -+ * hold @mn_info_lock when calling this function. Returns pointer to -+ * mn_info entry or %NULL on failure. -+ **/ -+struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo; -+ -+ if (!home_agent) -+ return NULL; -+ -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (!ipv6_addr_cmp(&minfo->ha, home_agent)) { -+ spin_unlock(&minfo->lock); -+ return minfo; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ return NULL; -+} -+ -+/** -+ * mipv6_mninfo_get_by_id - Lookup mn_info with id -+ * @id: DHAAD identifier -+ * -+ * Searches for a mn_info entry with @dhaad_id set to @id. You MUST -+ * hold @mn_info_lock when calling this function. Returns pointer to -+ * mn_info entry or %NULL on failure. -+ **/ -+struct mn_info *mipv6_mninfo_get_by_id(unsigned short id) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo = 0; -+ -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (minfo->dhaad_id == id) { -+ spin_unlock(&minfo->lock); -+ return minfo; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ return NULL; -+} -+ -+/** -+ * mipv6_mninfo_add - Adds a new home info for MN -+ * @ifindex: Interface for home address -+ * @home_addr: Home address of MN, must be set -+ * @plen: prefix length of the home address, must be set -+ * @isathome : home address at home -+ * @lifetime: lifetime of the home address, 0 is infinite -+ * @ha: home agent for the home address -+ * @ha_plen: prefix length of home agent's address, can be zero -+ * @ha_lifetime: Lifetime of the home address, 0 is infinite -+ * -+ * The function adds a new home info entry for MN, allowing it to -+ * register the home address with the home agent. Starts home -+ * registration process. If @ha is %ADDRANY, DHAAD is performed to -+ * find a home agent. Returns 0 on success, a negative value -+ * otherwise. Caller MUST NOT hold @mn_info_lock or -+ * @addrconf_hash_lock. -+ **/ -+void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, -+ int isathome, unsigned long lifetime, struct in6_addr *ha, -+ int ha_plen, unsigned long ha_lifetime, int man_conf) -+{ -+ struct mn_info *minfo; -+ struct in6_addr coa; -+ -+ DEBUG_FUNC(); -+ -+ write_lock_bh(&mn_info_lock); -+ if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL){ -+ DEBUG(1, "MN info already exists"); -+ write_unlock_bh(&mn_info_lock); -+ return; -+ } -+ minfo = kmalloc(sizeof(struct mn_info), GFP_ATOMIC); -+ if (!minfo) { -+ write_unlock_bh(&mn_info_lock); -+ return; -+ } -+ memset(minfo, 0, sizeof(struct mn_info)); -+ spin_lock_init(&minfo->lock); -+ -+ -+ ipv6_addr_copy(&minfo->home_addr, home_addr); -+ -+ if (ha) -+ ipv6_addr_copy(&minfo->ha, ha); -+ if (ha_plen < 128 && ha_plen > 0) -+ minfo->home_plen = ha_plen; -+ else minfo->home_plen = 64; -+ -+ minfo->ifindex_user = ifindex; /* Ifindex for tunnel interface */ -+ minfo->ifindex = ifindex; /* Interface on which home address is currently conf'd */ -+ /* TODO: we should get home address lifetime from somewhere */ -+ /* minfo->home_addr_expires = jiffies + lifetime * HZ; */ -+ -+ /* manual configuration flag cannot be unset by dynamic updates -+ * from prefix advertisements -+ */ -+ if (!minfo->man_conf) minfo->man_conf = man_conf; -+ minfo->is_at_home = isathome; -+ -+ list_add(&minfo->list, &mn_info_list); -+ write_unlock_bh(&mn_info_lock); -+ -+ if (mipv6_get_care_of_address(home_addr, &coa) == 0) -+ init_home_registration(home_addr, &coa); -+} -+ -+/** -+ * mipv6_mninfo_del - Delete home info for MN -+ * @home_addr : Home address or prefix -+ * @del_dyn_only : Delete only dynamically created home entries -+ * -+ * Deletes every mn_info entry that matches the first plen bits of -+ * @home_addr. Returns number of deleted entries on success and a -+ * negative value otherwise. Caller MUST NOT hold @mn_info_lock. -+ **/ -+int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only) -+{ -+ struct list_head *lh, *next; -+ struct mn_info *minfo; -+ int ret = -1; -+ if (!home_addr) -+ return -1; -+ -+ write_lock(&mn_info_lock); -+ -+ list_for_each_safe(lh, next, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ if (ipv6_addr_cmp(&minfo->home_addr, home_addr) == 0 -+ && ((!minfo->man_conf && del_dyn_only) || !del_dyn_only)){ -+ list_del(&minfo->list); -+ kfree(minfo); -+ ret++; -+ } -+ } -+ write_unlock(&mn_info_lock); -+ return ret; -+} -+ -+void mipv6_mn_set_home(int ifindex, struct in6_addr *homeaddr, int plen, -+ struct in6_addr *homeagent, int ha_plen) -+{ -+ mipv6_mninfo_add(ifindex, homeaddr, plen, 0, 0, -+ homeagent, ha_plen, 0, 1); -+} -+ -+static int skip_dad(struct in6_addr *addr) -+{ -+ struct mn_info *minfo; -+ int ret = 0; -+ -+ if (addr == NULL) { -+ DEBUG(DBG_CRITICAL, "Null argument"); -+ return 0; -+ } -+ read_lock_bh(&mn_info_lock); -+ if ((minfo = mipv6_mninfo_get_by_home(addr)) != NULL) { -+ if ((minfo->is_at_home != MN_NOT_AT_HOME) && (minfo->has_home_reg)) -+ ret = 1; -+ DEBUG(DBG_INFO, "minfo->is_at_home = %d, minfo->has_home_reg = %d", -+ minfo->is_at_home, minfo->has_home_reg); -+ } -+ read_unlock_bh(&mn_info_lock); -+ -+ return ret; -+} -+/** -+ * mipv6_mn_is_home_addr - Determines if addr is node's home address -+ * @addr: IPv6 address -+ * -+ * Returns 1 if addr is node's home address. Otherwise returns zero. -+ **/ -+int mipv6_mn_is_home_addr(struct in6_addr *addr) -+{ -+ int ret = 0; -+ -+ if (addr == NULL) { -+ DEBUG(DBG_CRITICAL, "Null argument"); -+ return -1; -+ } -+ read_lock_bh(&mn_info_lock); -+ if (mipv6_mninfo_get_by_home(addr)) -+ ret = 1; -+ read_unlock_bh(&mn_info_lock); -+ -+ return (ret); -+} -+ -+/** -+ * mipv6_mn_is_at_home - determine if node is home for a home address -+ * @home_addr : home address of MN -+ * -+ * Returns 1 if home address in question is in the home network, 0 -+ * otherwise. Caller MUST NOT not hold @mn_info_lock. -+ **/ -+int mipv6_mn_is_at_home(struct in6_addr *home_addr) -+{ -+ struct mn_info *minfo; -+ int ret = 0; -+ read_lock_bh(&mn_info_lock); -+ if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) { -+ spin_lock(&minfo->lock); -+ ret = (minfo->is_at_home == MN_AT_HOME); -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+ return ret; -+} -+void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg) -+{ -+ struct mn_info *minfo; -+ read_lock_bh(&mn_info_lock); -+ -+ if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) { -+ spin_lock(&minfo->lock); -+ minfo->has_home_reg = has_home_reg; -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+} -+ -+static int mn_inet6addr_event( -+ struct notifier_block *nb, unsigned long event, void *ptr) -+{ -+ struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)ptr; -+ -+ switch (event) { -+ case NETDEV_UP: -+ /* Is address a valid coa ?*/ -+ if (!(ifp->flags & IFA_F_TENTATIVE)) -+ mipv6_mdet_finalize_ho(&ifp->addr, -+ ifp->idev->dev->ifindex); -+ else if(skip_dad(&ifp->addr)) -+ ifp->flags &= ~IFA_F_TENTATIVE; -+ break; -+ case NETDEV_DOWN: -+#if 0 -+ /* This is useless with manually configured home -+ addresses, which will not expire -+ */ -+ mipv6_mninfo_del(&ifp->addr, 0); -+#endif -+ break; -+ -+ } -+ -+ return NOTIFY_DONE; -+} -+ -+struct notifier_block mipv6_mn_inet6addr_notifier = { -+ mn_inet6addr_event, -+ NULL, -+ 0 /* check if using zero is ok */ -+}; -+ -+static void mipv6_get_saddr_hook(struct in6_addr *homeaddr) -+{ -+ int found = 0, reiter = 0; -+ struct list_head *lh; -+ struct mn_info *minfo = NULL; -+ struct in6_addr coa; -+ -+ read_lock_bh(&mn_info_lock); -+restart: -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ if ((ipv6_addr_scope(homeaddr) != ipv6_addr_scope(&minfo->home_addr)) -+ || ipv6_chk_addr(&minfo->home_addr, NULL) == 0) -+ continue; -+ -+ spin_lock(&minfo->lock); -+ if (minfo->is_at_home == MN_AT_HOME || minfo->has_home_reg) { -+ if ((mipv6_use_topol_corr_hoaddr && -+ minfo->is_at_home == MN_AT_HOME) || -+ (mipv6_use_preconfigured_hoaddr && -+ minfo->man_conf) || -+ (!(mipv6_use_preconfigured_hoaddr || -+ mipv6_use_topol_corr_hoaddr) || reiter)) { -+ spin_unlock(&minfo->lock); -+ ipv6_addr_copy(homeaddr, &minfo->home_addr); -+ found = 1; -+ break; -+ } -+ } -+ spin_unlock(&minfo->lock); -+ } -+ if (!found && !reiter) { -+ reiter = 1; -+ goto restart; -+ } -+ -+ if (!found && minfo && -+ !mipv6_get_care_of_address(&minfo->home_addr, &coa)) { -+ ipv6_addr_copy(homeaddr, &coa); -+ } -+ read_unlock_bh(&mn_info_lock); -+ -+ DEBUG(DBG_DATADUMP, "Source address selection: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(homeaddr)); -+ return; -+} -+ -+static void mv_home_addr(void *arg) -+{ -+ mm_segment_t oldfs; -+ int err = 0, new_if = 0; -+ struct list_head *lh, *next; -+ struct ifr_holder *ifrh; -+ LIST_HEAD(list); -+ -+ DEBUG(DBG_INFO, "mipv6 move home address task"); -+ -+ spin_lock_bh(&ifrh_lock); -+ list_splice_init(&ifrh_list, &list); -+ spin_unlock_bh(&ifrh_lock); -+ -+ oldfs = get_fs(); set_fs(KERNEL_DS); -+ list_for_each_safe(lh, next, &list) { -+ ifrh = list_entry(lh, struct ifr_holder, list); -+ if (ifrh->old_ifi) { -+ new_if = ifrh->ifr.ifr6_ifindex; -+ ifrh->ifr.ifr6_ifindex = ifrh->old_ifi; -+ err = addrconf_del_ifaddr(&ifrh->ifr); -+ ifrh->ifr.ifr6_ifindex = new_if; -+ if (err < 0) -+ DEBUG(DBG_WARNING, "removal of home address %x:%x:%x:%x:%x:%x:%x:%x from" -+ " old interface %d failed with status %d", -+ NIPV6ADDR(&ifrh->ifr.ifr6_addr), ifrh->old_ifi, err); -+ } -+ if(!err) { -+ err = addrconf_add_ifaddr(&ifrh->ifr); -+ } -+ if (ifrh->ho) { -+ DEBUG(DBG_INFO, "Calling mobile_node moved after moving home address to new if"); -+ mipv6_mobile_node_moved(ifrh->ho); -+ } -+ list_del(&ifrh->list); -+ kfree(ifrh); -+ } -+ set_fs(oldfs); -+ -+ if (err < 0) -+ DEBUG(DBG_WARNING, "adding of home address to a new interface %d failed %d", new_if, err); -+ else { -+ DEBUG(DBG_WARNING, "adding of home address to a new interface OK"); -+ } -+} -+ -+struct dhaad_halist { -+ struct list_head list; -+ struct in6_addr addr; -+ int retry; -+}; -+ -+/* clear all has from candidate list. do this when a new dhaad reply -+ * is received. */ -+int mipv6_mn_flush_ha_candidate(struct list_head *ha) -+{ -+ struct list_head *p, *tmp; -+ struct dhaad_halist *e; -+ -+ list_for_each_safe(p, tmp, ha) { -+ e = list_entry(p, struct dhaad_halist, list); -+ list_del(p); -+ kfree(e); -+ e = NULL; -+ } -+ return 0; -+} -+ -+/* add new ha to candidates. only done when dhaad reply is received. */ -+int mipv6_mn_add_ha_candidate(struct list_head *ha, struct in6_addr *addr) -+{ -+ struct dhaad_halist *e; -+ -+ e = kmalloc(sizeof(*e), GFP_ATOMIC); -+ memset(e, 0, sizeof(*e)); -+ ipv6_addr_copy(&e->addr, addr); -+ -+ list_add_tail(&e->list, ha); -+ return 0; -+} -+ -+#define MAX_RETRIES_PER_HA 3 -+ -+/* get next ha candidate. this is done when dhaad reply has been -+ * received and we want to register with the best available ha. */ -+int mipv6_mn_get_ha_candidate(struct list_head *ha, struct in6_addr *addr) -+{ -+ struct list_head *p; -+ -+ list_for_each(p, ha) { -+ struct dhaad_halist *e; -+ e = list_entry(p, typeof(*e), list); -+ if (e->retry >= 0 && e->retry < MAX_RETRIES_PER_HA) { -+ ipv6_addr_copy(addr, &e->addr); -+ return 0; -+ } -+ } -+ return -1; -+} -+ -+/* change candidate status. if registration with ha fails, we -+ * increase retry for ha candidate. if retry is >= 3 we set it to -1 -+ * (failed), do get_ha_candidate() again */ -+int mipv6_mn_try_ha_candidate(struct list_head *ha, struct in6_addr *addr) -+{ -+ struct list_head *p; -+ -+ list_for_each(p, ha) { -+ struct dhaad_halist *e; -+ e = list_entry(p, typeof(*e), list); -+ if (ipv6_addr_cmp(addr, &e->addr) == 0) { -+ if (e->retry >= MAX_RETRIES_PER_HA) e->retry = -1; -+ else if (e->retry >= 0) e->retry++; -+ return 0; -+ } -+ } -+ return -1; -+} -+ -+/** -+ * mipv6_mn_get_bulifetime - Get lifetime for a binding update -+ * @home_addr: home address for BU -+ * @coa: care-of address for BU -+ * @flags: flags used for BU -+ * -+ * Returns maximum lifetime for BUs determined by the lifetime of -+ * care-of address and the lifetime of home address. -+ **/ -+__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, struct in6_addr *coa, -+ __u8 flags) -+{ -+ struct inet6_ifaddr *ifp_hoa, *ifp_coa; -+ __u32 lifetime = (flags & MIPV6_BU_F_HOME ? -+ HA_BU_DEF_LIFETIME : CN_BU_DEF_LIFETIME); -+ -+ ifp_hoa = ipv6_get_ifaddr(home_addr, NULL); -+ if(!ifp_hoa) { -+ DEBUG(DBG_INFO, "home address missing"); -+ return 0; -+ } -+ if (!(ifp_hoa->flags & IFA_F_PERMANENT)){ -+ if (ifp_hoa->valid_lft) -+ lifetime = min_t(__u32, lifetime, ifp_hoa->valid_lft); -+ else -+ DEBUG(DBG_ERROR, "Zero lifetime for home address"); -+ } -+ in6_ifa_put(ifp_hoa); -+ -+ ifp_coa = ipv6_get_ifaddr(coa, NULL); -+ if (!ifp_coa) { -+ DEBUG(DBG_INFO, "care-of address missing"); -+ return 0; -+ } -+ if (!(ifp_coa->flags & IFA_F_PERMANENT)) { -+ if(ifp_coa->valid_lft) -+ lifetime = min_t(__u32, lifetime, ifp_coa->valid_lft); -+ else -+ DEBUG(DBG_ERROR, -+ "Zero lifetime for care-of address"); -+ } -+ in6_ifa_put(ifp_coa); -+ -+ DEBUG(DBG_INFO, "Lifetime for binding is %ld", lifetime); -+ return lifetime; -+} -+ -+static int -+mipv6_mn_tnl_rcv_send_bu_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ struct ipv6hdr *inner; -+ struct ipv6hdr *outer = skb->nh.ipv6h; -+ struct mn_info *minfo = NULL; -+ __u32 lifetime; -+ __u8 user_flags = 0; -+ -+ DEBUG_FUNC(); -+ -+ if (!is_mip6_tnl(t)) -+ return IP6_TNL_ACCEPT; -+ -+ if (!mip6node_cnf.accept_ret_rout) { -+ DEBUG(DBG_INFO, "Return routability administratively disabled" -+ " not doing route optimization"); -+ return IP6_TNL_ACCEPT; -+ } -+ if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*inner))) -+ return IP6_TNL_DROP; -+ -+ inner = (struct ipv6hdr *)skb->h.raw; -+ -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_home(&inner->daddr); -+ -+ if (!minfo) { -+ DEBUG(DBG_WARNING, "MN info missing"); -+ read_unlock(&mn_info_lock); -+ return IP6_TNL_ACCEPT; -+ } -+ DEBUG(DBG_DATADUMP, "MIPV6 MN: Received a tunneled IPv6 packet" -+ " to %x:%x:%x:%x:%x:%x:%x:%x," -+ " from %x:%x:%x:%x:%x:%x:%x:%x with\n tunnel header" -+ "daddr: %x:%x:%x:%x:%x:%x:%x:%x," -+ "saddr: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&inner->daddr), NIPV6ADDR(&inner->saddr), -+ NIPV6ADDR(&outer->daddr), NIPV6ADDR(&outer->saddr)); -+ -+ spin_lock(&minfo->lock); -+ -+ /* We don't send bus in response to all tunneled packets */ -+ -+ if (!ipv6_addr_cmp(&minfo->ha, &inner->saddr)) { -+ spin_unlock(&minfo->lock); -+ read_unlock(&mn_info_lock); -+ DEBUG(DBG_ERROR, "HA BUG: Received a tunneled packet " -+ "originally sent by home agent, not sending BU"); -+ return IP6_TNL_ACCEPT; -+ } -+ spin_unlock(&minfo->lock); -+ read_unlock(&mn_info_lock); -+ -+ DEBUG(DBG_DATADUMP, "Sending BU to correspondent node"); -+ -+ user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0; -+ -+ if (inner->nexthdr != IPPROTO_DSTOPTS && -+ inner->nexthdr != IPPROTO_MOBILITY) { -+ struct in6_addr coa; -+ /* Don't start RR when receiving ICMP error messages */ -+ if (inner->nexthdr == IPPROTO_ICMPV6) { -+ int ptr = (u8*)(inner+1) - skb->data; -+ u8 type; -+ -+ if (skb_copy_bits(skb, -+ ptr+offsetof(struct icmp6hdr, -+ icmp6_type), -+ &type, 1) -+ || !(type & ICMPV6_INFOMSG_MASK)) { -+ return IP6_TNL_ACCEPT; -+ } -+ } -+ lifetime = mipv6_mn_get_bulifetime(&inner->daddr, -+ &outer->daddr, 0); -+ if (lifetime && -+ !mipv6_get_care_of_address(&inner->daddr, &coa)) { -+ write_lock(&bul_lock); -+ mipv6_send_bu(&inner->daddr, &inner->saddr, &coa, -+ INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, -+ user_flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ } -+ } -+ DEBUG(DBG_DATADUMP, "setting rcv_tunnel flag in skb"); -+ skb->security |= MIPV6_RCV_TUNNEL; -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_send_bu_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_DECAP, -+ IP6_TNL_PRI_FIRST, -+ mipv6_mn_tnl_rcv_send_bu_hook -+}; -+ -+static int -+mipv6_mn_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_encapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_mn_tnl_xmit_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_ENCAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_mn_tnl_xmit_stats_hook -+}; -+ -+static int -+mipv6_mn_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_decapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_DECAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_mn_tnl_rcv_stats_hook -+}; -+ -+static void mn_check_tunneled_packet(struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ /* If tunnel flag was set */ -+ if (skb->security & MIPV6_RCV_TUNNEL) { -+ struct in6_addr coa; -+ __u32 lifetime; -+ __u8 user_flags = 0; -+ int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data; -+ int len = skb->len - ptr; -+ __u8 nexthdr = skb->nh.ipv6h->nexthdr; -+ -+ if (len < 0) -+ return; -+ -+ ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, len); -+ if (ptr < 0) -+ return; -+ -+ if (!mip6node_cnf.accept_ret_rout) { -+ DEBUG(DBG_INFO, "Return routability administratively disabled"); -+ return; -+ } -+ if (nexthdr == IPPROTO_MOBILITY) -+ return; -+ -+ /* Don't start RR when receiving ICMP error messages */ -+ if (nexthdr == IPPROTO_ICMPV6) { -+ u8 type; -+ -+ if (skb_copy_bits(skb, -+ ptr+offsetof(struct icmp6hdr, -+ icmp6_type), -+ &type, 1) -+ || !(type & ICMPV6_INFOMSG_MASK)) { -+ return; -+ } -+ } -+ user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0; -+ mipv6_get_care_of_address(&skb->nh.ipv6h->daddr, &coa); -+ lifetime = mipv6_mn_get_bulifetime(&skb->nh.ipv6h->daddr, -+ &coa, 0); -+ -+ DEBUG(DBG_WARNING, "packet to address %x:%x:%x:%x:%x:%x:%x:%x" -+ "was tunneled. Sending BU to CN" -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&skb->nh.ipv6h->daddr), -+ NIPV6ADDR(&skb->nh.ipv6h->saddr)); -+ /* This should work also with home address option */ -+ -+ write_lock(&bul_lock); -+ mipv6_send_bu(&skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr, -+ &coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, user_flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ } -+} -+ -+static int sched_mv_home_addr_task(struct in6_addr *haddr, int plen_new, -+ int newif, int oldif, struct handoff *ho) -+{ -+ int alloc_size; -+ struct ifr_holder *ifrh; -+ -+ alloc_size = sizeof(*ifrh) + (ho ? sizeof(*ho): 0); -+ if ((ifrh = kmalloc(alloc_size, GFP_ATOMIC)) == NULL) { -+ DEBUG(DBG_ERROR, "Out of memory"); -+ return -1; -+ } -+ if (ho) { -+ ifrh->ho = (struct handoff *)((struct ifr_holder *)(ifrh + 1)); -+ memcpy(ifrh->ho, ho, sizeof(*ho)); -+ } else -+ ifrh->ho = NULL; -+ -+ /* must queue task to avoid deadlock with rtnl */ -+ ifrh->ifr.ifr6_ifindex = newif; -+ ifrh->ifr.ifr6_prefixlen = plen_new; -+ ipv6_addr_copy(&ifrh->ifr.ifr6_addr, haddr); -+ ifrh->old_ifi = oldif; -+ -+ spin_lock_bh(&ifrh_lock); -+ list_add_tail(&ifrh->list, &ifrh_list); -+ spin_unlock_bh(&ifrh_lock); -+ -+ schedule_task(&mv_home_addr_task); -+ -+ return 0; -+} -+ -+static void send_ret_home_ns(struct in6_addr *ha_addr, -+ struct in6_addr *home_addr, -+ int ifindex) -+{ -+ struct in6_addr nil; -+ struct in6_addr mcaddr; -+ struct net_device *dev = dev_get_by_index(ifindex); -+ if (!dev) -+ return; -+ memset(&nil, 0, sizeof(nil)); -+ addrconf_addr_solict_mult(home_addr, &mcaddr); -+ ndisc_send_ns(dev, NULL, home_addr, &mcaddr, &nil); -+ dev_put(dev); -+} -+ -+static inline int ha_is_reachable(int ifindex, struct in6_addr *ha) -+{ -+ struct net_device *dev; -+ int reachable = 0; -+ -+ dev = dev_get_by_index(ifindex); -+ if (dev) { -+ struct neighbour *neigh; -+ if ((neigh = ndisc_get_neigh(dev, ha)) != NULL) { -+ read_lock_bh(&neigh->lock); -+ if (neigh->nud_state&NUD_VALID) -+ reachable = 1; -+ read_unlock_bh(&neigh->lock); -+ neigh_release(neigh); -+ } -+ dev_put(dev); -+ } -+ return reachable; -+} -+ -+static int mn_ha_handoff(struct handoff *ho) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo; -+ struct in6_addr *coa= ho->coa; -+ int wait_mv_home = 0; -+ -+ read_lock_bh(&mn_info_lock); -+ list_for_each(lh, &mn_info_list) { -+ __u8 has_home_reg; -+ int ifindex; -+ struct in6_addr ha; -+ __u8 athome; -+ __u32 lifetime; -+ struct mipv6_bul_entry *entry = NULL; -+ -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ has_home_reg = minfo->has_home_reg; -+ ifindex = minfo->ifindex; -+ ipv6_addr_copy(&ha, &minfo->ha); -+ -+ if (mipv6_prefix_compare(&ho->rtr_addr, &minfo->home_addr, -+ ho->plen)) { -+ if (minfo->has_home_reg) -+ athome = minfo->is_at_home = MN_RETURNING_HOME; -+ else -+ athome = minfo->is_at_home = MN_AT_HOME; -+ coa = &minfo->home_addr; -+ -+ spin_unlock(&minfo->lock); -+#if 0 -+ /* Cancel prefix solicitation, rtr is our HA */ -+ mipv6_pfx_cancel_send(&ho->rtr_addr, ifindex); -+#endif -+ minfo->ifindex = ho->ifindex; -+ -+ if (minfo->has_home_reg && -+ !ha_is_reachable(ho->ifindex, &minfo->ha)) { -+ send_ret_home_ns(&minfo->ha, -+ &minfo->home_addr, -+ ho->ifindex); -+ mipv6_mdet_set_curr_rtr_reachable(0); -+ wait_mv_home++; -+ } -+ if (ifindex != ho->ifindex){ -+ wait_mv_home++; -+ DEBUG(DBG_INFO, -+ "Moving home address back to " -+ "the home interface"); -+ sched_mv_home_addr_task(&minfo->home_addr, -+ 128, -+ ho->ifindex, -+ ifindex, ho); -+ } -+ if (!has_home_reg || wait_mv_home) -+ continue; -+ -+ lifetime = 0; -+ -+ } else { -+ athome = minfo->is_at_home = MN_NOT_AT_HOME; -+ if (minfo->ifindex_user != minfo->ifindex) { -+ DEBUG(DBG_INFO, "Scheduling home address move to virtual interface"); -+ sched_mv_home_addr_task(&minfo->home_addr, -+ 128, -+ minfo->ifindex_user, -+ minfo->ifindex, ho); /* Is minfo->ifindex correct */ -+ -+ wait_mv_home++; -+ } -+ minfo->ifindex = minfo->ifindex_user; -+ spin_unlock(&minfo->lock); -+ if (wait_mv_home) -+ continue; -+ if (!has_home_reg && -+ init_home_registration(&minfo->home_addr, -+ ho->coa)) { -+ continue; -+ } -+ lifetime = mipv6_mn_get_bulifetime(&minfo->home_addr, -+ ho->coa, -+ MIPV6_BU_F_HOME); -+ -+ } -+ write_lock(&bul_lock); -+ if (!(entry = mipv6_bul_get(&ha, &minfo->home_addr)) || -+ !(entry->flags & MIPV6_BU_F_HOME)) { -+ DEBUG(DBG_ERROR, -+ "Unable to find home registration for " -+ "home address: %x:%x:%x:%x:%x:%x:%x:%x!\n", -+ NIPV6ADDR(&minfo->home_addr)); -+ write_unlock(&bul_lock); -+ continue; -+ } -+ DEBUG(DBG_INFO, "Sending home de ? %d registration for " -+ "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" -+ "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " -+ "with lifetime %ld", -+ (athome != MN_NOT_AT_HOME), -+ NIPV6ADDR(&entry->home_addr), -+ NIPV6ADDR(&entry->cn_addr), lifetime); -+ mipv6_send_bu(&entry->home_addr, &entry->cn_addr, -+ coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, entry->flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ -+ } -+ read_unlock_bh(&mn_info_lock); -+ return wait_mv_home; -+} -+/** -+ * mn_cn_handoff - called for every bul entry to send BU to CN -+ * @rawentry: bul entry -+ * @args: handoff event -+ * @sortkey: -+ * -+ * Since MN can have many home addresses and home networks, every BUL -+ * entry needs to be checked -+ **/ -+int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey) -+{ -+ struct mipv6_bul_entry *entry = (struct mipv6_bul_entry *)rawentry; -+ struct in6_addr *coa = (struct in6_addr *)args; -+ -+ DEBUG_FUNC(); -+ -+ /* Home registrations already handled by mn_ha_handoff */ -+ if (entry->flags & MIPV6_BU_F_HOME) -+ return ITERATOR_CONT; -+ -+ /* BUL is locked by mipv6_mobile_node_moved which calls us -+ through mipv6_bul_iterate */ -+ -+ if (mipv6_prefix_compare(coa, -+ &entry->home_addr, -+ 64)) { -+ mipv6_send_bu(&entry->home_addr, &entry->cn_addr, -+ &entry->home_addr, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, entry->flags, 0, -+ NULL); -+ } else { -+ u32 lifetime = mipv6_mn_get_bulifetime(&entry->home_addr, -+ coa, -+ entry->flags); -+ mipv6_send_bu(&entry->home_addr, &entry->cn_addr, -+ coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, entry->flags, -+ lifetime, NULL); -+ } -+ return ITERATOR_CONT; -+} -+ -+ -+int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey) -+{ -+ struct mipv6_bul_entry *bul = (struct mipv6_bul_entry *)rawentry; -+ struct bul_inval_args *arg = (struct bul_inval_args *)args; -+ -+ DEBUG_FUNC(); -+ -+ if (!ipv6_addr_cmp(arg->cn, &bul->cn_addr) && -+ (!ipv6_addr_cmp(arg->mn, &bul->home_addr) || -+ !ipv6_addr_cmp(arg->mn, &bul->coa))) { -+ if (arg->all_rr_states || !bul->rr || -+ (bul->rr->rr_state != RR_INIT && -+ bul->rr->rr_state != RR_DONE)) { -+ bul->state = ACK_ERROR; -+ bul->callback = bul_entry_expired; -+ bul->callback_time = jiffies + -+ DUMB_CN_BU_LIFETIME * HZ; -+ bul->expire = bul->callback_time; -+ DEBUG(DBG_INFO, "BUL entry set to ACK_ERROR"); -+ mipv6_bul_reschedule(bul); -+ } -+ } -+ return ITERATOR_CONT; -+} -+/** -+ * init_home_registration - start Home Registration process -+ * @home_addr: home address -+ * @coa: care-of address -+ * -+ * Checks whether we have a Home Agent address for this home address. -+ * If not starts Dynamic Home Agent Address Discovery. Otherwise -+ * tries to register with home agent if not already registered. -+ * Returns 1, if home registration process is started and 0 otherwise -+ **/ -+int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa) -+{ -+ struct mn_info *hinfo; -+ struct in6_addr ha; -+ __u8 man_conf; -+ int ifindex; -+ __u32 lifetime; -+ __u8 user_flags = 0, flags; -+ -+ DEBUG_FUNC(); -+ -+ read_lock_bh(&mn_info_lock); -+ if ((hinfo = mipv6_mninfo_get_by_home(home_addr)) == NULL) { -+ DEBUG(DBG_ERROR, "No mn_info found for address: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(home_addr)); -+ read_unlock_bh(&mn_info_lock); -+ return -ENOENT; -+ } -+ spin_lock(&hinfo->lock); -+ if (mipv6_prefix_compare(&hinfo->home_addr, coa, hinfo->home_plen)) { -+ spin_unlock(&hinfo->lock); -+ read_unlock_bh(&mn_info_lock); -+ DEBUG(DBG_INFO, "Adding home address, MN at home"); -+ return 1; -+ } -+ if (ipv6_addr_any(&hinfo->ha)) { -+ int dhaad_id = mipv6_get_dhaad_id(); -+ hinfo->dhaad_id = dhaad_id; -+ spin_unlock(&hinfo->lock); -+ mipv6_icmpv6_send_dhaad_req(home_addr, hinfo->home_plen, dhaad_id); -+ read_unlock_bh(&mn_info_lock); -+ DEBUG(DBG_INFO, -+ "Home Agent address not set, initiating DHAAD"); -+ return 1; -+ } -+ ipv6_addr_copy(&ha, &hinfo->ha); -+ man_conf = hinfo->man_conf; -+ ifindex = hinfo->ifindex; -+ spin_unlock(&hinfo->lock); -+ read_unlock_bh(&mn_info_lock); -+#if 0 -+ if (man_conf) -+ mipv6_pfx_add_ha(&ha, coa, ifindex); -+#endif -+ if (mipv6_bul_exists(&ha, home_addr)) { -+ DEBUG(DBG_INFO, "BU already sent to HA"); -+ return 0; -+ } -+ /* user flags received through sysctl */ -+ user_flags |= mip6node_cnf.bu_lladdr ? MIPV6_BU_F_LLADDR : 0; -+ user_flags |= mip6node_cnf.bu_keymgm ? MIPV6_BU_F_KEYMGM : 0; -+ -+ flags = MIPV6_BU_F_HOME | MIPV6_BU_F_ACK | user_flags; -+ -+ lifetime = mipv6_mn_get_bulifetime(home_addr, coa, flags); -+ -+ DEBUG(DBG_INFO, "Sending initial home registration for " -+ "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" -+ "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " -+ "with lifetime %ld, prefixlength %d", -+ NIPV6ADDR(home_addr), NIPV6ADDR(&ha), lifetime, 0); -+ -+ write_lock_bh(&bul_lock); -+ mipv6_send_bu(home_addr, &ha, coa, INITIAL_BINDACK_DAD_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, flags, lifetime, NULL); -+ write_unlock_bh(&bul_lock); -+ -+ return 1; -+} -+ -+/** -+ * mipv6_mobile_node_moved - Send BUs to all HAs and CNs -+ * @ho: handoff structure contains the new and previous routers -+ * -+ * Event for handoff. Sends BUs everyone on Binding Update List. -+ **/ -+int mipv6_mobile_node_moved(struct handoff *ho) -+{ -+#if 0 -+ int bu_to_prev_router = 1; -+#endif -+ int dummy; -+ -+ DEBUG_FUNC(); -+ -+ ma_ctl_upd_iface(ho->ifindex, -+ MA_IFACE_CURRENT | MA_IFACE_HAS_ROUTER, &dummy); -+ -+ /* First send BU to HA, then to all other nodes that are on BU list */ -+ if (mn_ha_handoff(ho) != 0) -+ return 0; /* Wait for move home address task */ -+#if 0 -+ /* Add current care-of address to mn_info list, if current router acts -+ as a HA.*/ -+ -+ if (ho->home_address && bu_to_prev_router) -+ mipv6_mninfo_add(ho->coa, ho->plen, -+ MN_AT_HOME, 0, &ho->rtr_addr, -+ ho->plen, ROUTER_BU_DEF_LIFETIME, -+ 0); -+ -+#endif -+ return 0; -+} -+ -+/** -+ * mipv6_mn_send_home_na - send NA when returning home -+ * @haddr: home address to advertise -+ * -+ * After returning home, MN must advertise all its valid addresses in -+ * home link to all nodes. -+ **/ -+void mipv6_mn_send_home_na(struct in6_addr *haddr) -+{ -+ struct net_device *dev = NULL; -+ struct in6_addr mc_allnodes; -+ struct mn_info *hinfo = NULL; -+ -+ read_lock(&mn_info_lock); -+ hinfo = mipv6_mninfo_get_by_home(haddr); -+ if (!hinfo) { -+ read_unlock(&mn_info_lock); -+ return; -+ } -+ spin_lock(&hinfo->lock); -+ hinfo->is_at_home = MN_AT_HOME; -+ dev = dev_get_by_index(hinfo->ifindex); -+ spin_unlock(&hinfo->lock); -+ read_unlock(&mn_info_lock); -+ if (dev == NULL) { -+ DEBUG(DBG_ERROR, "Send home_na: device not found."); -+ return; -+ } -+ -+ ipv6_addr_all_nodes(&mc_allnodes); -+ ndisc_send_na(dev, NULL, &mc_allnodes, haddr, 0, 0, 1, 1); -+ dev_put(dev); -+} -+ -+static int mn_use_hao(struct in6_addr *daddr, struct in6_addr *saddr) -+{ -+ struct mipv6_bul_entry *entry; -+ struct mn_info *minfo = NULL; -+ int add_ha = 0; -+ -+ read_lock_bh(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_home(saddr); -+ if (minfo && minfo->is_at_home != MN_AT_HOME) { -+ read_lock_bh(&bul_lock); -+ if ((entry = mipv6_bul_get(daddr, saddr)) == NULL) { -+ read_unlock_bh(&bul_lock); -+ read_unlock_bh(&mn_info_lock); -+ return add_ha; -+ } -+ add_ha = (entry->state != ACK_ERROR && -+ (!entry->rr || entry->rr->rr_state == RR_DONE || -+ entry->flags & MIPV6_BU_F_HOME)); -+ read_unlock_bh(&bul_lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+ return add_ha; -+} -+ -+static int -+mn_dev_event(struct notifier_block *nb, unsigned long event, void *ptr) -+{ -+ struct net_device *dev = ptr; -+ struct list_head *lh; -+ struct mn_info *minfo; -+ int newif = 0; -+ -+ /* here are probably the events we need to worry about */ -+ switch (event) { -+ case NETDEV_UP: -+ DEBUG(DBG_DATADUMP, "New netdevice %s registered.", dev->name); -+ if (dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) -+ ma_ctl_add_iface(dev->ifindex); -+ -+ break; -+ case NETDEV_GOING_DOWN: -+ DEBUG(DBG_DATADUMP, "Netdevice %s disappeared.", dev->name); -+ /* -+ * Go through mn_info list and move all home addresses on the -+ * netdev going down to a new device. This will make it -+ * practically impossible for the home address to return home, -+ * but allow MN to retain its connections using the address. -+ */ -+ -+ read_lock_bh(&mn_info_lock); -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (minfo->ifindex == dev->ifindex) { -+ if (sched_mv_home_addr_task(&minfo->home_addr, 128, -+ minfo->ifindex_user, -+ 0, NULL) < 0) { -+ minfo->ifindex = 0; -+ spin_unlock(&minfo->lock); -+ read_unlock_bh(&mn_info_lock); -+ return NOTIFY_DONE; -+ } else { -+ minfo->ifindex = minfo->ifindex_user; -+ if (minfo->is_at_home) { -+ minfo->is_at_home = 0; -+ -+ } -+ newif = minfo->ifindex_user; -+ } -+ } -+ spin_unlock(&minfo->lock); -+ } -+ -+ read_unlock_bh(&mn_info_lock); -+ } -+ ma_ctl_upd_iface(dev->ifindex, MA_IFACE_NOT_PRESENT, &newif); -+ mipv6_mdet_del_if(dev->ifindex); -+ -+ return NOTIFY_DONE; -+} -+ -+struct notifier_block mipv6_mn_dev_notifier = { -+ mn_dev_event, -+ NULL, -+ 0 /* check if using zero is ok */ -+}; -+ -+static void deprecate_addr(struct mn_info *minfo) -+{ -+ /* -+ * Lookup address from IPv6 address list and set deprecated flag -+ */ -+ -+} -+ -+/* -+ * Required because we can only modify addresses after the packet is -+ * constructed. We otherwise mess with higher level protocol -+ * pseudoheaders. With strict protocol layering life would be SO much -+ * easier! -+ */ -+static unsigned int modify_xmit_addrs(unsigned int hooknum, -+ struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ int (*okfn) (struct sk_buff *)) -+{ -+ struct sk_buff *skb = *pskb; -+ -+ DEBUG_FUNC(); -+ -+ if (skb) { -+ struct ipv6hdr *hdr = skb->nh.ipv6h; -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; -+ struct mipv6_bul_entry *bule; -+ struct in6_addr *daddr; -+ -+ if (!ipv6_addr_any(&opt->hoa)) -+ daddr = &opt->hoa; -+ else -+ daddr = &hdr->daddr; -+ -+ /* We don't consult bul when sending a BU to avoid deadlock, since -+ * BUL is already locked. -+ */ -+ -+ -+ if (opt->mipv6_flags & MIPV6_SND_HAO && -+ !(opt->mipv6_flags & MIPV6_SND_BU)) { -+ write_lock(&bul_lock); -+ bule = mipv6_bul_get(daddr, &hdr->saddr); -+ if (!bule) { -+ write_unlock(&bul_lock); -+ return NF_ACCEPT; -+ } -+ if (!bule->rr || bule->rr->rr_state == RR_DONE || -+ bule->flags & MIPV6_BU_F_HOME) { -+ DEBUG(DBG_DATADUMP, -+ "Replace source address with CoA and reroute"); -+ ipv6_addr_copy(&hdr->saddr, &bule->coa); -+ skb->nfcache |= NFC_ALTERED; -+ } -+ write_unlock(&bul_lock); -+ } else if (opt->mipv6_flags & MIPV6_SND_HAO) { -+ mipv6_get_care_of_address(&hdr->saddr, &hdr->saddr); -+ skb->nfcache |= NFC_ALTERED; -+ } -+ } -+ return NF_ACCEPT; -+} -+ -+/* We set a netfilter hook so that we can modify outgoing packet's -+ * source addresses -+ */ -+struct nf_hook_ops addr_modify_hook_ops = { -+ {NULL, NULL}, /* List head, no predecessor, no successor */ -+ modify_xmit_addrs, -+ PF_INET6, -+ NF_IP6_LOCAL_OUT, -+ NF_IP6_PRI_FIRST /* Should be of EXTREMELY high priority since we -+ * do not want to mess with IPSec (possibly -+ * implemented as packet filter) -+ */ -+}; -+ -+#define MN_INFO_LEN 77 -+ -+static int mn_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct list_head *p; -+ struct mn_info *minfo; -+ int len = 0, skip = 0; -+ -+ DEBUG_FUNC(); -+ -+ read_lock_bh(&mn_info_lock); -+ list_for_each(p, &mn_info_list) { -+ if (len < offset / MN_INFO_LEN) { -+ skip++; -+ continue; -+ } -+ if (len >= length) -+ break; -+ minfo = list_entry(p, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ len += sprintf(buffer + len, "%02d %08x%08x%08x%08x %02x " -+ "%08x%08x%08x%08x %d %d\n", -+ minfo->ifindex, -+ ntohl(minfo->home_addr.s6_addr32[0]), -+ ntohl(minfo->home_addr.s6_addr32[1]), -+ ntohl(minfo->home_addr.s6_addr32[2]), -+ ntohl(minfo->home_addr.s6_addr32[3]), -+ minfo->home_plen, -+ ntohl(minfo->ha.s6_addr32[0]), -+ ntohl(minfo->ha.s6_addr32[1]), -+ ntohl(minfo->ha.s6_addr32[2]), -+ ntohl(minfo->ha.s6_addr32[3]), -+ minfo->is_at_home, minfo->has_home_reg); -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % MN_INFO_LEN; -+ -+ len -= offset % MN_INFO_LEN; -+ -+ if (len > length) -+ len = length; -+ if (len < 0) -+ len = 0; -+ -+ return len; -+} -+ -+int mipv6_mn_ha_nd_update(struct net_device *dev, -+ struct in6_addr *ha, u8 *lladdr) -+{ -+ int valid = 0; -+ struct neighbour *neigh; -+ if ((neigh = ndisc_get_neigh(dev, ha))) { -+ read_lock(&neigh->lock); -+ valid = neigh->nud_state & NUD_VALID; -+ read_unlock(&neigh->lock); -+ if (!valid && lladdr) -+ neigh_update(neigh, lladdr, NUD_REACHABLE, 0, 1); -+ neigh_release(neigh); -+ } -+ return valid; -+} -+ -+int mipv6_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) -+{ -+ struct mn_info *minfo; -+ -+ if (!(minfo = mipv6_mninfo_get_by_home(&ifp->addr)) || -+ ipv6_addr_any(&minfo->ha)) -+ return 0; -+ -+ if (mipv6_mn_ha_nd_update(ifp->idev->dev, &minfo->ha, lladdr)) -+ mipv6_mdet_retrigger_ho(); -+ return 1; -+} -+ -+int __init mipv6_mn_init(void) -+{ -+ struct net_device *dev; -+ -+ DEBUG_FUNC(); -+ -+ if (mipv6_add_tnl_to_ha()) -+ return -ENODEV; -+ -+ mipv6_bul_init(MIPV6_BUL_SIZE); -+ mip6_fn.mn_use_hao = mn_use_hao; -+ mip6_fn.mn_check_tunneled_packet = mn_check_tunneled_packet; -+ INIT_TQUEUE(&mv_home_addr_task, mv_home_addr, NULL); -+ -+ ma_ctl_init(); -+ for (dev = dev_base; dev; dev = dev->next) { -+ if (dev->flags & IFF_UP && -+ dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) { -+ ma_ctl_add_iface(dev->ifindex); -+ } -+ } -+ DEBUG(DBG_INFO, "Multiaccess support initialized"); -+ -+ register_netdevice_notifier(&mipv6_mn_dev_notifier); -+ register_inet6addr_notifier(&mipv6_mn_inet6addr_notifier); -+ -+ ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_send_bu_ops); -+ ip6ip6_tnl_register_hook(&mipv6_mn_tnl_xmit_stats_ops); -+ ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_stats_ops); -+ -+ MIPV6_SETCALL(mipv6_set_home, mipv6_mn_set_home); -+ -+ mipv6_initialize_mdetect(); -+ -+ /* COA to home transformation hook */ -+ MIPV6_SETCALL(mipv6_get_home_address, mipv6_get_saddr_hook); -+ MIPV6_SETCALL(mipv6_mn_ha_probe, mipv6_mn_ha_probe); -+ MIPV6_SETCALL(mipv6_is_home_addr, mipv6_mn_is_home_addr); -+ proc_net_create("mip6_mninfo", 0, mn_proc_info); -+ /* Set packet modification hook (source addresses) */ -+ nf_register_hook(&addr_modify_hook_ops); -+ -+ return 0; -+} -+ -+void __exit mipv6_mn_exit(void) -+{ -+ struct list_head *lh, *tmp; -+ struct mn_info *minfo; -+ DEBUG_FUNC(); -+ -+ mip6_fn.mn_use_hao = NULL; -+ mip6_fn.mn_check_tunneled_packet = NULL; -+ -+ MIPV6_RESETCALL(mipv6_set_home); -+ MIPV6_RESETCALL(mipv6_get_home_address); -+ MIPV6_RESETCALL(mipv6_mn_ha_probe); -+ MIPV6_RESETCALL(mipv6_is_home_addr); -+ nf_unregister_hook(&addr_modify_hook_ops); -+ proc_net_remove("mip6_mninfo"); -+ mipv6_shutdown_mdetect(); -+ ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_stats_ops); -+ ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_xmit_stats_ops); -+ ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_send_bu_ops); -+ ma_ctl_clean(); -+ -+ unregister_inet6addr_notifier(&mipv6_mn_inet6addr_notifier); -+ unregister_netdevice_notifier(&mipv6_mn_dev_notifier); -+ write_lock_bh(&mn_info_lock); -+ -+ list_for_each_safe(lh, tmp, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ if (minfo->is_at_home == MN_NOT_AT_HOME) -+ deprecate_addr(minfo); -+ list_del(&minfo->list); -+ kfree(minfo); -+ } -+ write_unlock_bh(&mn_info_lock); -+ mipv6_bul_exit(); -+ flush_scheduled_tasks(); -+ mipv6_del_tnl_to_ha(); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mn.h linux-2.4.25/net/ipv6/mobile_ip6/mn.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mn.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mn.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,96 @@ -+/* -+ * MIPL Mobile IPv6 Mobile Node header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MN_H -+#define _MN_H -+ -+#include <linux/in6.h> -+ -+/* constants for sending of BUs*/ -+#define HA_BU_DEF_LIFETIME 10000 -+#define CN_BU_DEF_LIFETIME 420 /* Max lifetime for RR bindings from RFC 3775 */ -+#define DUMB_CN_BU_LIFETIME 600 /* BUL entry lifetime in case of dumb CN */ -+#define ROUTER_BU_DEF_LIFETIME 30 /* For packet forwarding from previous coa */ -+#define ERROR_DEF_LIFETIME DUMB_CN_BU_LIFETIME -+ -+extern rwlock_t mn_info_lock; -+ -+#define MN_NOT_AT_HOME 0 -+#define MN_RETURNING_HOME 1 -+#define MN_AT_HOME 2 -+ -+/* -+ * Mobile Node information record -+ */ -+struct mn_info { -+ struct in6_addr home_addr; -+ struct in6_addr ha; -+ __u8 home_plen; -+ __u8 is_at_home; -+ __u8 has_home_reg; -+ __u8 man_conf; -+ int ifindex; -+ int ifindex_user; -+ unsigned long home_addr_expires; -+ unsigned short dhaad_id; -+ struct list_head list; -+ spinlock_t lock; -+}; -+ -+/* prototypes for interface functions */ -+int mipv6_mn_init(void); -+void mipv6_mn_exit(void); -+ -+struct handoff; -+ -+/* Interface to movement detection */ -+int mipv6_mobile_node_moved(struct handoff *ho); -+ -+void mipv6_mn_send_home_na(struct in6_addr *haddr); -+/* Init home reg. with coa */ -+int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa); -+ -+/* mn_info functions that require locking by caller */ -+struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr); -+ -+struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent); -+ -+struct mn_info *mipv6_mninfo_get_by_id(unsigned short id); -+ -+/* "safe" mn_info functions */ -+void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, -+ int isathome, unsigned long lifetime, struct in6_addr *ha, -+ int ha_plen, unsigned long ha_lifetime, int man_conf); -+ -+int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only); -+ -+void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg); -+ -+int mipv6_mn_is_at_home(struct in6_addr *addr); -+ -+int mipv6_mn_is_home_addr(struct in6_addr *addr); -+ -+__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, -+ struct in6_addr *coa, __u8 flags); -+int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey); -+ -+int mipv6_mn_ha_nd_update(struct net_device *dev, -+ struct in6_addr *ha, u8 *lladdr); -+ -+struct bul_inval_args { -+ int all_rr_states; -+ struct in6_addr *cn; -+ struct in6_addr *mn; -+}; -+ -+int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey); -+ -+#endif /* _MN_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr.h linux-2.4.25/net/ipv6/mobile_ip6/mobhdr.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,101 @@ -+/* -+ * MIPL Mobile IPv6 Mobility Header send and receive -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MOBHDR_H -+#define _MOBHDR_H -+ -+#include <net/mipv6.h> -+ -+/* RR states for mipv6_send_bu() */ -+#define RR_INIT 0x00 -+#define RR_WAITH 0x01 -+#define RR_WAITC 0x02 -+#define RR_WAITHC 0x13 -+#define RR_DONE 0x10 -+ -+#define MH_UNKNOWN_CN 1 -+#define MH_AUTH_FAILED 2 -+#define MH_SEQUENCE_MISMATCH 3 -+ -+struct mipv6_bul_entry; -+struct sk_buff; -+ -+int mipv6_mh_common_init(void); -+void mipv6_mh_common_exit(void); -+int mipv6_mh_mn_init(void); -+void mipv6_mh_mn_exit(void); -+ -+struct mipv6_mh_opt { -+ struct mipv6_mo_alt_coa *alt_coa; -+ struct mipv6_mo_nonce_indices *nonce_indices; -+ struct mipv6_mo_bauth_data *auth_data; -+ struct mipv6_mo_br_advice *br_advice; -+ int freelen; -+ int totlen; -+ u8 *next_free; -+ u8 data[0]; -+}; -+ -+struct mobopt { -+ struct mipv6_mo_alt_coa *alt_coa; -+ struct mipv6_mo_nonce_indices *nonce_indices; -+ struct mipv6_mo_bauth_data *auth_data; -+ struct mipv6_mo_br_advice *br_advice; -+}; -+ -+struct mipv6_mh_opt *alloc_mh_opts(int totlen); -+int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data); -+int parse_mo_tlv(void *mos, int len, struct mobopt *opts); -+int mipv6_add_pad(u8 *data, int n); -+ -+struct mipv6_auth_parm { -+ struct in6_addr *coa; -+ struct in6_addr *cn_addr; -+ __u8 *k_bu; -+}; -+ -+int send_mh(struct in6_addr *daddr, struct in6_addr *saddr, -+ u8 msg_type, u8 msg_len, u8 *msg, -+ struct in6_addr *hao_addr, struct in6_addr *rth_addr, -+ struct mipv6_mh_opt *ops, struct mipv6_auth_parm *parm); -+ -+int mipv6_mh_register(int type, int (*func)(struct sk_buff *, -+ struct in6_addr *, struct in6_addr *, -+ struct in6_addr *, struct in6_addr *, struct mipv6_mh *)); -+ -+void mipv6_mh_unregister(int type); -+ -+int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct mipv6_mh_opt *ops); -+ -+int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *coa, __u32 initdelay, -+ __u32 maxackdelay, __u8 exp, __u8 flags, -+ __u32 lifetime, struct mipv6_mh_opt *ops); -+ -+int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *home, __u8 status); -+ -+int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *auth_coa, struct in6_addr *rep_coa, -+ u8 status, u16 sequence, u32 lifetime, u8 *k_bu); -+ -+/* Binding Authentication Data Option routines */ -+#define MAX_HASH_LENGTH 20 -+#define MIPV6_RR_MAC_LENGTH 12 -+ -+int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *opt, __u8 *aud_data, __u8 *k_bu); -+ -+int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *opt, __u8 optlen, struct mipv6_mo_bauth_data *aud, -+ __u8 *k_bu); -+#endif /* _MOBHDR_H */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_common.c linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_common.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_common.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_common.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,1210 @@ -+/* -+ * Mobile IPv6 Mobility Header Common Functions -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id: s.mh_recv.c 1.159 02/10/16 15:01:29+03:00 antti@traci.mipl.mediapoli.com $ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/types.h> -+#include <linux/in6.h> -+#include <linux/skbuff.h> -+#include <linux/ipsec.h> -+#include <linux/init.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+#include <net/checksum.h> -+#include <net/protocol.h> -+ -+#include "stats.h" -+#include "debug.h" -+#include "mobhdr.h" -+#include "bcache.h" -+ -+#include "rr_crypto.h" -+#include "exthdrs.h" -+#include "config.h" -+ -+#define MIPV6_MH_MAX MIPV6_MH_BE -+struct mh_proto { -+ int (*func) (struct sk_buff *, -+ struct in6_addr *, struct in6_addr *, -+ struct in6_addr *, struct in6_addr *, -+ struct mipv6_mh *); -+}; -+ -+static struct mh_proto mh_rcv[MIPV6_MH_MAX]; -+ -+int mipv6_mh_register(int type, int (*func)(struct sk_buff *, -+ struct in6_addr *, struct in6_addr *, -+ struct in6_addr *, struct in6_addr *, struct mipv6_mh *)) -+{ -+ if (mh_rcv[type].func != NULL) -+ return -1; -+ -+ mh_rcv[type].func = func; -+ -+ return 0; -+} -+ -+void mipv6_mh_unregister(int type) -+{ -+ if (type < 0 || type > MIPV6_MH_MAX) -+ return; -+ -+ mh_rcv[type].func = NULL; -+} -+ -+struct socket *mipv6_mh_socket = NULL; -+ -+/* TODO: Fix fragmentation */ -+static int dstopts_getfrag( -+ const void *data, struct in6_addr *addr, -+ char *buff, unsigned int offset, unsigned int len) -+{ -+ memcpy(buff, data + offset, len); -+ return 0; -+} -+ -+struct mipv6_mh_opt *alloc_mh_opts(int totlen) -+{ -+ struct mipv6_mh_opt *ops; -+ -+ ops = kmalloc(sizeof(*ops) + totlen, GFP_ATOMIC); -+ if (ops == NULL) -+ return NULL; -+ -+ memset(ops, 0, sizeof(*ops)); -+ ops->next_free = ops->data; -+ ops->freelen = totlen; -+ -+ return ops; -+} -+ -+int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data) -+{ -+ struct mipv6_mo *mo; -+ -+ if (ops->next_free == NULL) { -+ DEBUG(DBG_ERROR, "No free room for option"); -+ return -ENOMEM; -+ } -+ if (ops->freelen < len + 2) { -+ DEBUG(DBG_ERROR, "No free room for option"); -+ return -ENOMEM; -+ } -+ else { -+ ops->freelen -= (len + 2); -+ ops->totlen += (len + 2); -+ } -+ -+ mo = (struct mipv6_mo *)ops->next_free; -+ mo->type = type; -+ mo->length = len; -+ -+ switch (type) { -+ case MIPV6_OPT_ALTERNATE_COA: -+ ops->alt_coa = (struct mipv6_mo_alt_coa *)mo; -+ ipv6_addr_copy(&ops->alt_coa->addr, (struct in6_addr *)data); -+ break; -+ case MIPV6_OPT_NONCE_INDICES: -+ DEBUG(DBG_INFO, "Added nonce indices pointer"); -+ ops->nonce_indices = (struct mipv6_mo_nonce_indices *)mo; -+ ops->nonce_indices->home_nonce_i = *(__u16 *)data; -+ ops->nonce_indices->careof_nonce_i = *((__u16 *)data + 1); -+ break; -+ case MIPV6_OPT_AUTH_DATA: -+ DEBUG(DBG_INFO, "Added opt auth_data pointer"); -+ ops->auth_data = (struct mipv6_mo_bauth_data *)mo; -+ break; -+ case MIPV6_OPT_BIND_REFRESH_ADVICE: -+ ops->br_advice = (struct mipv6_mo_br_advice *)mo; -+ ops->br_advice->refresh_interval = htons(*(u16 *)data); -+ break; -+ default: -+ DEBUG(DBG_ERROR, "Unknow option type"); -+ break; -+ } -+ -+ if (ops->freelen == 0) -+ ops->next_free = NULL; -+ else -+ ops->next_free += (len + 2); -+ -+ return 0; -+} -+ -+/* -+ * Calculates required padding with xn + y requirement with offset -+ */ -+static inline int optpad(int xn, int y, int offset) -+{ -+ return ((y - offset) & (xn - 1)); -+} -+ -+static int option_pad(int type, int offset) -+{ -+ if (type == MIPV6_OPT_ALTERNATE_COA) -+ return optpad(8, 6, offset); /* 8n + 6 */ -+ if (type == MIPV6_OPT_BIND_REFRESH_ADVICE || -+ type == MIPV6_OPT_NONCE_INDICES) -+ return optpad(2, 0, offset); /* 2n */ -+ return 0; -+} -+ -+/* -+ * Add Pad1 or PadN option to data -+ */ -+int mipv6_add_pad(u8 *data, int n) -+{ -+ struct mipv6_mo_padn *padn; -+ -+ if (n <= 0) return 0; -+ if (n == 1) { -+ *data = MIPV6_OPT_PAD1; -+ return 1; -+ } -+ padn = (struct mipv6_mo_padn *)data; -+ padn->type = MIPV6_OPT_PADN; -+ padn->length = n - 2; -+ memset(padn->data, 0, n - 2); -+ return n; -+} -+ -+/* -+ * Write options to mobility header buffer -+ */ -+static int prepare_mh_opts(u8 *optdata, int off, struct mipv6_mh_opt *ops) -+{ -+ u8 *nextopt = optdata; -+ int offset = off, pad = 0; -+ -+ if (ops == NULL) { -+ nextopt = NULL; -+ return -1; -+ } -+ -+ if (ops->alt_coa) { -+ pad = option_pad(MIPV6_OPT_ALTERNATE_COA, offset); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->alt_coa, sizeof(struct mipv6_mo_alt_coa)); -+ nextopt += sizeof(struct mipv6_mo_alt_coa); -+ offset += pad + sizeof(struct mipv6_mo_alt_coa); -+ } -+ -+ if (ops->br_advice) { -+ pad = option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->br_advice, sizeof(struct mipv6_mo_br_advice)); -+ nextopt += sizeof(struct mipv6_mo_br_advice); -+ offset += pad + sizeof(struct mipv6_mo_br_advice); -+ } -+ -+ if (ops->nonce_indices) { -+ pad = option_pad(MIPV6_OPT_NONCE_INDICES, offset); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->nonce_indices, sizeof(struct mipv6_mo_nonce_indices)); -+ nextopt += sizeof(struct mipv6_mo_nonce_indices); -+ offset += pad + sizeof(struct mipv6_mo_nonce_indices); -+ } -+ -+ if (ops->auth_data) { -+ /* This option should always be the last. Header -+ * length must be a multiple of 8 octects, so we pad -+ * if necessary. */ -+ pad = optpad(8, 0, offset + ops->auth_data->length + 2); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->auth_data, ops->auth_data->length + 2); -+ nextopt += ops->auth_data->length + 2; -+ } -+ nextopt = NULL; -+ -+ return 0; -+} -+ -+static int calculate_mh_opts(struct mipv6_mh_opt *ops, int mh_len) -+{ -+ int offset = mh_len; -+ -+ if (ops == NULL) -+ return 0; -+ -+ if (ops->alt_coa) -+ offset += sizeof(struct mipv6_mo_alt_coa) -+ + option_pad(MIPV6_OPT_ALTERNATE_COA, offset); -+ -+ if (ops->br_advice) -+ offset += sizeof(struct mipv6_mo_br_advice) -+ + option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset); -+ -+ if (ops->nonce_indices) -+ offset += sizeof(struct mipv6_mo_nonce_indices) -+ + option_pad(MIPV6_OPT_NONCE_INDICES, offset); -+ -+ if (ops->auth_data) /* no alignment */ -+ offset += ops->auth_data->length + 2; -+ -+ return offset - mh_len; -+} -+ -+/* -+ * -+ * Mobility Header Message send functions -+ * -+ */ -+ -+/** -+ * send_mh - builds and sends a MH msg -+ * -+ * @daddr: destination address for packet -+ * @saddr: source address for packet -+ * @msg_type: type of MH -+ * @msg_len: message length -+ * @msg: MH type specific data -+ * @hao_addr: home address for home address option -+ * @rth_addr: routing header address -+ * @ops: mobility options -+ * @parm: auth data -+ * -+ * Builds MH, appends the type specific msg data to the header and -+ * sends the packet with a home address option, if a home address was -+ * given. Returns 0, if everything succeeded and a negative error code -+ * otherwise. -+ **/ -+int send_mh(struct in6_addr *daddr, -+ struct in6_addr *saddr, -+ u8 msg_type, u8 msg_len, u8 *msg, -+ struct in6_addr *hao_addr, -+ struct in6_addr *rth_addr, -+ struct mipv6_mh_opt *ops, -+ struct mipv6_auth_parm *parm) -+{ -+ struct flowi fl; -+ struct mipv6_mh *mh; -+ struct sock *sk = mipv6_mh_socket->sk; -+ struct ipv6_txoptions *txopt = NULL; -+ int tot_len = sizeof(struct mipv6_mh) + msg_len; -+ int padded_len = 0, txopt_len = 0; -+ -+ DEBUG_FUNC(); -+ /* Add length of options */ -+ tot_len += calculate_mh_opts(ops, tot_len); -+ /* Needs to be a multiple of 8 octets */ -+ padded_len = tot_len + optpad(8, 0, tot_len); -+ -+ mh = sock_kmalloc(sk, padded_len, GFP_ATOMIC); -+ if (!mh) { -+ DEBUG(DBG_ERROR, "memory allocation failed"); -+ return -ENOMEM; -+ } -+ -+ memset(&fl, 0, sizeof(fl)); -+ fl.proto = IPPROTO_MOBILITY; -+ fl.fl6_dst = daddr; -+ fl.fl6_src = saddr; -+ fl.fl6_flowlabel = 0; -+ fl.oif = sk->bound_dev_if; -+ -+ if (hao_addr || rth_addr) { -+ __u8 *opt_ptr; -+ -+ if (hao_addr) -+ txopt_len += sizeof(struct mipv6_dstopt_homeaddr) + 6; -+ if (rth_addr) -+ txopt_len += sizeof(struct rt2_hdr); -+ -+ txopt_len += sizeof(*txopt); -+ txopt = sock_kmalloc(sk, txopt_len, GFP_ATOMIC); -+ if (txopt == NULL) { -+ DEBUG(DBG_ERROR, "No socket space left"); -+ sock_kfree_s(sk, mh, padded_len); -+ return -ENOMEM; -+ } -+ memset(txopt, 0, txopt_len); -+ txopt->tot_len = txopt_len; -+ opt_ptr = (__u8 *) (txopt + 1); -+ if (hao_addr) { -+ int holen = sizeof(struct mipv6_dstopt_homeaddr) + 6; -+ txopt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr; -+ txopt->opt_flen += holen; -+ opt_ptr += holen; -+ mipv6_append_dst1opts(txopt->dst1opt, saddr, -+ NULL, holen); -+ txopt->mipv6_flags = MIPV6_SND_HAO | MIPV6_SND_BU; -+ } -+ if (rth_addr) { -+ int rtlen = sizeof(struct rt2_hdr); -+ txopt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr; -+ txopt->opt_nflen += rtlen; -+ opt_ptr += rtlen; -+ mipv6_append_rt2hdr(txopt->srcrt2, rth_addr); -+ } -+ } -+ -+ /* Fill in the fields of MH */ -+ mh->payload = NEXTHDR_NONE; -+ mh->length = (padded_len >> 3) - 1; /* Units of 8 octets - 1 */ -+ mh->type = msg_type; -+ mh->reserved = 0; -+ mh->checksum = 0; -+ -+ memcpy(mh->data, msg, msg_len); -+ prepare_mh_opts(mh->data + msg_len, msg_len + sizeof(*mh), ops); -+ /* If BAD is present, this is already done. */ -+ mipv6_add_pad((u8 *)mh + tot_len, padded_len - tot_len); -+ -+ if (parm && parm->k_bu && ops && ops->auth_data) { -+ /* Calculate the position of the authorization data before adding checksum*/ -+ mipv6_auth_build(parm->cn_addr, parm->coa, (__u8 *)mh, -+ (__u8 *)mh + padded_len - MIPV6_RR_MAC_LENGTH, parm->k_bu); -+ } -+ /* Calculate the MH checksum */ -+ mh->checksum = csum_ipv6_magic(fl.fl6_src, fl.fl6_dst, -+ padded_len, IPPROTO_MOBILITY, -+ csum_partial((char *)mh, padded_len, 0)); -+ ip6_build_xmit(sk, dstopts_getfrag, mh, &fl, padded_len, txopt, 255, -+ MSG_DONTWAIT); -+ /* dst cache must be cleared so RR messages can be routed through -+ different interfaces */ -+ sk_dst_reset(sk); -+ -+ if (txopt_len) -+ sock_kfree_s(sk, txopt, txopt_len); -+ sock_kfree_s(sk, mh, padded_len); -+ return 0; -+} -+ -+/** -+ * mipv6_send_brr - send a Binding Refresh Request -+ * @saddr: source address for BRR -+ * @daddr: destination address for BRR -+ * @ops: mobility options -+ * -+ * Sends a binding request. On a mobile node, use the mobile node's -+ * home address for @saddr. Returns 0 on success, negative on -+ * failure. -+ **/ -+int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct mipv6_mh_opt *ops) -+{ -+ struct mipv6_mh_brr br; -+ -+ memset(&br, 0, sizeof(br)); -+ /* We don't need to explicitly add a RH to brr, since it will be -+ * included automatically, if a BCE exists -+ */ -+ MIPV6_INC_STATS(n_brr_sent); -+ return send_mh(daddr, saddr, MIPV6_MH_BRR, sizeof(br), (u8 *)&br, -+ NULL, NULL, ops, NULL); -+} -+ -+/** -+ * mipv6_send_ba - send a Binding Acknowledgement -+ * @saddr: source address for BA -+ * @daddr: destination address for BA -+ * @reply_coa: destination care-of address of MN -+ * @auth_coa: care-of address of MN used for authentication -+ * @status: status field value -+ * @sequence: sequence number from BU -+ * @lifetime: granted lifetime for binding in seconds -+ * @ops: mobility options -+ * -+ * Send a binding acknowledgement. On a mobile node, use the mobile -+ * node's home address for saddr. Returns 0 on success, non-zero on -+ * failure. -+ **/ -+int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *auth_coa, struct in6_addr *rep_coa, -+ u8 status, u16 sequence, u32 lifetime, u8 *k_bu) -+{ -+ struct mipv6_mh_ba ba; -+ struct mipv6_auth_parm parm; -+ struct mipv6_mh_opt *ops = NULL; -+ int ops_len = 0, ret = 0; -+ struct mipv6_bce bc_entry; -+ int coming_home = 0; -+ int bypass_tnl = 0; -+ -+ memset(&ba, 0, sizeof(ba)); -+ -+ ba.status = status; -+ ba.sequence = htons(sequence); -+ ba.lifetime = htons(lifetime >> 2); -+ -+ DEBUG(DBG_INFO, "sending a status %d BA %s authenticator to MN \n" -+ "%x:%x:%x:%x:%x:%x:%x:%x at care of address \n" -+ "%x:%x:%x:%x:%x:%x:%x:%x : with lifetime %d and \n" -+ " sequence number %d", -+ status, k_bu ? "with" : "without", -+ NIPV6ADDR(daddr), NIPV6ADDR(auth_coa), lifetime, sequence); -+ -+ memset(&parm, 0, sizeof(parm)); -+ parm.coa = auth_coa; -+ parm.cn_addr = saddr; -+ -+ if (k_bu) { -+ ops_len += sizeof(struct mipv6_mo_bauth_data) + -+ MIPV6_RR_MAC_LENGTH; -+ parm.k_bu = k_bu; -+ } -+ -+ if (mip6node_cnf.binding_refresh_advice) { -+ ops_len += sizeof(struct mipv6_mo_br_advice); -+ } -+ if (ops_len) { -+ ops = alloc_mh_opts(ops_len); -+ if (ops == NULL) { -+ DEBUG(DBG_WARNING, "Out of memory"); -+ return -ENOMEM; -+ } -+ if (mip6node_cnf.binding_refresh_advice > 0) { -+ if (append_mh_opt(ops, MIPV6_OPT_BIND_REFRESH_ADVICE, 2, -+ &mip6node_cnf.binding_refresh_advice) < 0) { -+ DEBUG(DBG_WARNING, "Adding BRA failed"); -+ if (ops) -+ kfree(ops); -+ return -ENOMEM; -+ } -+ } -+ if (k_bu) { -+ if (append_mh_opt(ops, MIPV6_OPT_AUTH_DATA, -+ MIPV6_RR_MAC_LENGTH, NULL) < 0) { -+ DEBUG(DBG_WARNING, "Adding BAD failed"); -+ if (ops) -+ kfree(ops); -+ return -ENOMEM; -+ } -+ } -+ } -+ coming_home = !ipv6_addr_cmp(rep_coa, daddr); -+ -+ bypass_tnl = (coming_home && -+ !mipv6_bcache_get(daddr, saddr, &bc_entry) && -+ bc_entry.flags&MIPV6_BU_F_HOME && -+ status >= 128); -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_del) -+ mip6_fn.bce_tnl_rt_del(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ if (coming_home) -+ ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba, -+ NULL, NULL, ops, &parm); -+ else -+ ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba, -+ NULL, rep_coa, ops, &parm); -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_add) -+ mip6_fn.bce_tnl_rt_add(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ if (ret == 0) { -+ if (status < 128) { -+ MIPV6_INC_STATS(n_ba_sent); -+ } else { -+ MIPV6_INC_STATS(n_ban_sent); -+ } -+ } -+ -+ if (ops) -+ kfree(ops); -+ -+ return 0; -+} -+ -+/** -+ * mipv6_send_be - send a Binding Error message -+ * @saddr: source address for BE -+ * @daddr: destination address for BE -+ * @home: Home Address in offending packet (if any) -+ * -+ * Sends a binding error. On a mobile node, use the mobile node's -+ * home address for @saddr. Returns 0 on success, negative on -+ * failure. -+ **/ -+int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *home, __u8 status) -+{ -+ struct mipv6_mh_be be; -+ int ret = 0; -+ struct mipv6_bce bc_entry; -+ int bypass_tnl = 0; -+ -+ if (ipv6_addr_is_multicast(daddr)) -+ return -EINVAL; -+ -+ memset(&be, 0, sizeof(be)); -+ be.status = status; -+ if (home) -+ ipv6_addr_copy(&be.home_addr, home); -+ -+ if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0 && -+ bc_entry.flags&MIPV6_BU_F_HOME) -+ bypass_tnl = 1; -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_del) -+ mip6_fn.bce_tnl_rt_del(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ ret = send_mh(daddr, saddr, MIPV6_MH_BE, sizeof(be), (u8 *)&be, -+ NULL, NULL, NULL, NULL); -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_add) -+ mip6_fn.bce_tnl_rt_add(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ if (ret == 0) -+ MIPV6_INC_STATS(n_be_sent); -+ -+ return ret; -+} -+ -+/** -+ * mipv6_send_addr_test - send a HoT or CoT message -+ * @saddr: source address -+ * @daddr: destination address -+ * @msg_type: HoT or CoT message -+ * @init: HoTI or CoTI message -+ * -+ * Send a reply to HoTI or CoTI message. -+ **/ -+static int mipv6_send_addr_test(struct in6_addr *saddr, -+ struct in6_addr *daddr, -+ int msg_type, -+ struct mipv6_mh_addr_ti *init) -+{ -+ u_int8_t *kgen_token = NULL; -+ struct mipv6_mh_addr_test addr_test; -+ struct mipv6_rr_nonce *nonce; -+ struct mipv6_mh_opt *ops = NULL; -+ int ret = 0; -+ -+ DEBUG_FUNC(); -+ -+ if ((nonce = mipv6_rr_get_new_nonce())== NULL) { -+ DEBUG(DBG_WARNING, "Nonce creation failed"); -+ return 0; -+ } -+ if (mipv6_rr_cookie_create(daddr, &kgen_token, nonce->index)) { -+ DEBUG(DBG_WARNING, "No cookie"); -+ return 0; -+ } -+ -+ addr_test.nonce_index = nonce->index; -+ memcpy(addr_test.init_cookie, init->init_cookie, -+ MIPV6_RR_COOKIE_LENGTH); -+ memcpy(addr_test.kgen_token, kgen_token, -+ MIPV6_RR_COOKIE_LENGTH); -+ -+ /* No options defined */ -+ ret = send_mh(daddr, saddr, msg_type, sizeof(addr_test), -+ (u8 *)&addr_test, NULL, NULL, ops, NULL); -+ -+ if (ret == 0) { -+ if (msg_type == MIPV6_MH_HOT) { -+ MIPV6_INC_STATS(n_hot_sent); -+ } else { -+ MIPV6_INC_STATS(n_cot_sent); -+ } -+ } -+ -+ return 0; -+} -+ -+static void bc_cache_add(int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ __u8 ba_status = SUCCESS; -+ -+ if (lifetime > MAX_RR_BINDING_LIFE) -+ lifetime = MAX_RR_BINDING_LIFE; -+ -+ if (mipv6_bcache_add(ifindex, daddr, haddr, coa, lifetime, -+ sequence, flags, CACHE_ENTRY) != 0) { -+ DEBUG(DBG_ERROR, "binding failed."); -+ ba_status = INSUFFICIENT_RESOURCES; -+ } -+ -+ if (flags & MIPV6_BU_F_ACK) { -+ DEBUG(DBG_INFO, "sending ack (code=%d)", ba_status); -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence, -+ lifetime, k_bu); -+ } -+} -+ -+static void bc_cn_home_add(int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, -+ HOME_REGISTRATION_NOT_SUPPORTED, -+ sequence, lifetime, k_bu); -+} -+ -+static void bc_cache_delete(struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu) -+{ -+ __u8 status = SUCCESS; -+ -+ /* Cached Care-of Address Deregistration */ -+ if (mipv6_bcache_exists(haddr, daddr) == CACHE_ENTRY) { -+ mipv6_bcache_delete(haddr, daddr, CACHE_ENTRY); -+ } else { -+ DEBUG(DBG_INFO, "entry is not in cache"); -+ status = REASON_UNSPECIFIED; -+ } -+ if (flags & MIPV6_BU_F_ACK) { -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, -+ 0, k_bu); -+ } -+} -+ -+static void bc_cn_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu) -+{ -+} -+ -+/** -+ * parse_mo_tlv - Parse TLV-encoded Mobility Options -+ * @mos: pointer to Mobility Options -+ * @len: total length of options -+ * @opts: structure to store option pointers -+ * -+ * Parses Mobility Options passed in @mos. Stores pointers in @opts -+ * to all valid mobility options found in @mos. Unknown options and -+ * padding (%MIPV6_OPT_PAD1 and %MIPV6_OPT_PADN) is ignored and -+ * skipped. -+ **/ -+int parse_mo_tlv(void *mos, int len, struct mobopt *opts) -+{ -+ struct mipv6_mo *curr = (struct mipv6_mo *)mos; -+ int left = len; -+ -+ while (left > 0) { -+ int optlen = 0; -+ if (curr->type == MIPV6_OPT_PAD1) -+ optlen = 1; -+ else -+ optlen = 2 + curr->length; -+ -+ if (optlen > left) -+ goto bad; -+ -+ switch (curr->type) { -+ case MIPV6_OPT_PAD1: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_PAD1 at %x", curr); -+ break; -+ case MIPV6_OPT_PADN: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_PADN at %x", curr); -+ break; -+ case MIPV6_OPT_ALTERNATE_COA: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_ACOA at %x", curr); -+ opts->alt_coa = (struct mipv6_mo_alt_coa *)curr; -+ break; -+ case MIPV6_OPT_NONCE_INDICES: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_NONCE_INDICES at %x", curr); -+ opts->nonce_indices = -+ (struct mipv6_mo_nonce_indices *)curr; -+ break; -+ case MIPV6_OPT_AUTH_DATA: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_AUTH_DATA at %x", curr); -+ opts->auth_data = (struct mipv6_mo_bauth_data *)curr; -+ break; -+ case MIPV6_OPT_BIND_REFRESH_ADVICE: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_BIND_REFRESH_ADVICE at %x", curr); -+ opts->br_advice = (struct mipv6_mo_br_advice *)curr; -+ break; -+ default: -+ DEBUG(DBG_INFO, "MO Unknown option type %d at %x, ignoring.", -+ curr->type, curr); -+ /* unknown mobility option, ignore and skip */ -+ } -+ -+ (u8 *)curr += optlen; -+ left -= optlen; -+ } -+ -+ if (left == 0) -+ return 0; -+ bad: -+ return -1; -+} -+ -+/* -+ * -+ * Mobility Header Message handlers -+ * -+ */ -+ -+static int mipv6_handle_mh_testinit(struct sk_buff *skb, -+ struct in6_addr *cn, -+ struct in6_addr *lcoa, -+ struct in6_addr *saddr, -+ struct in6_addr *fcoa, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_addr_ti *ti = (struct mipv6_mh_addr_ti *)mh->data; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ DEBUG_FUNC(); -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*ti); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "Mobility Header length less than H/C TestInit"); -+ return -1; -+ } -+ if (!mip6node_cnf.accept_ret_rout) { -+ DEBUG(DBG_INFO, "Return routability administratively disabled"); -+ return -1; -+ } -+ if (lcoa || fcoa) { -+ DEBUG(DBG_INFO, "H/C TestInit has HAO or RTH2, dropped."); -+ return -1; -+ } -+ -+ if (mh->type == MIPV6_MH_HOTI) { -+ MIPV6_INC_STATS(n_hoti_rcvd); -+ return mipv6_send_addr_test(cn, saddr, MIPV6_MH_HOT, ti); -+ } else if (mh->type == MIPV6_MH_COTI) { -+ MIPV6_INC_STATS(n_coti_rcvd); -+ return mipv6_send_addr_test(cn, saddr, MIPV6_MH_COT, ti); -+ } else -+ return -1; /* Impossible to get here */ -+} -+ -+/** -+ * mipv6_handle_mh_bu - Binding Update handler -+ * @src: care-of address of sender -+ * @dst: our address -+ * @haddr: home address of sender -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ * Handles Binding Update. Packet and offset to option are passed. -+ * Returns 0 on success, otherwise negative. -+ **/ -+static int mipv6_handle_mh_bu(struct sk_buff *skb, -+ struct in6_addr *dst, -+ struct in6_addr *unused, -+ struct in6_addr *haddr, -+ struct in6_addr *coaddr, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_bu *bu = (struct mipv6_mh_bu *)mh->data; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ int auth = 0; -+ int dereg; /* Is this deregistration? */ -+ int addr_type; -+ -+ struct mipv6_bce bc_entry; -+ struct in6_addr *coa, *reply_coa; -+ __u8 *key_bu = NULL; /* RR BU authentication key */ -+ __u8 flags = bu->flags; -+ __u16 sequence; -+ __u32 lifetime; -+ __u16 nonce_ind = (__u16) -1; -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*bu); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "Mobility Header length less than BU"); -+ MIPV6_INC_STATS(n_bu_drop.invalid); -+ return -1; -+ } -+ -+ addr_type = ipv6_addr_type(haddr); -+ if (addr_type&IPV6_ADDR_LINKLOCAL || !(addr_type&IPV6_ADDR_UNICAST)) -+ return -EINVAL; -+ -+ /* If HAO not present, CoA == HAddr */ -+ if (coaddr == NULL) -+ coa = haddr; -+ else { -+ coa = coaddr; -+ addr_type = ipv6_addr_type(coa); -+ if (addr_type&IPV6_ADDR_LINKLOCAL || -+ !(addr_type&IPV6_ADDR_UNICAST)) -+ return -EINVAL; -+ } -+ reply_coa = coa; -+ -+ sequence = ntohs(bu->sequence); -+ if (bu->lifetime == 0xffff) -+ lifetime = 0xffffffff; -+ else -+ lifetime = ntohs(bu->lifetime) << 2; -+ -+ dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0); -+ -+ if (opt_len > 0) { -+ struct mobopt opts; -+ memset(&opts, 0, sizeof(opts)); -+ if (parse_mo_tlv(bu + 1, opt_len, &opts) < 0) { -+ MIPV6_INC_STATS(n_bu_drop.invalid); -+ return -1; -+ } -+ /* -+ * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_NONCE_INDICES, -+ * MIPV6_OPT_ALT_COA -+ */ -+ if (opts.alt_coa) { -+ coa = &opts.alt_coa->addr; -+ dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0); -+ } -+ addr_type = ipv6_addr_type(coa); -+ if (addr_type&IPV6_ADDR_LINKLOCAL || -+ !(addr_type&IPV6_ADDR_UNICAST)) -+ return -EINVAL; -+ -+ if (flags & MIPV6_BU_F_HOME) { -+ if (opts.nonce_indices) -+ return -1; -+ } else { -+ u8 ba_status = 0; -+ u8 *h_ckie = NULL, *c_ckie = NULL; /* Home and care-of cookies */ -+ -+ /* BUs to CN MUST include authorization data and nonce indices options */ -+ if (!opts.auth_data || !opts.nonce_indices) { -+ DEBUG(DBG_WARNING, -+ "Route optimization BU without authorization material, aborting processing"); -+ return MH_AUTH_FAILED; -+ } -+ if (mipv6_rr_cookie_create( -+ haddr, &h_ckie, opts.nonce_indices->home_nonce_i) < 0) { -+ DEBUG(DBG_WARNING, -+ "mipv6_rr_cookie_create failed for home cookie"); -+ ba_status = EXPIRED_HOME_NONCE_INDEX; -+ } -+ nonce_ind = opts.nonce_indices->home_nonce_i; -+ /* Don't create the care-of cookie, if MN deregisters */ -+ if (!dereg && mipv6_rr_cookie_create( -+ coa, &c_ckie, -+ opts.nonce_indices->careof_nonce_i) < 0) { -+ DEBUG(DBG_WARNING, -+ "mipv6_rr_cookie_create failed for coa cookie"); -+ if (ba_status == 0) -+ ba_status = EXPIRED_CAREOF_NONCE_INDEX; -+ else -+ ba_status = EXPIRED_NONCES; -+ } -+ if (ba_status == 0) { -+ if (dereg) -+ key_bu = mipv6_rr_key_calc(h_ckie, NULL); -+ else -+ key_bu = mipv6_rr_key_calc(h_ckie, c_ckie); -+ mh->checksum = 0;/* TODO: Don't mangle the packet */ -+ if (key_bu && mipv6_auth_check( -+ dst, coa, (__u8 *)mh, msg_len + sizeof(*mh), opts.auth_data, key_bu) == 0) { -+ DEBUG(DBG_INFO, "mipv6_auth_check OK for BU"); -+ auth = 1; -+ } else { -+ DEBUG(DBG_WARNING, -+ "BU Authentication failed"); -+ } -+ } -+ if (h_ckie) -+ kfree(h_ckie); -+ if (c_ckie) -+ kfree(c_ckie); -+ if (ba_status != 0) { -+ MIPV6_INC_STATS(n_bu_drop.auth); -+ mipv6_send_ba(dst, haddr, coa, -+ reply_coa, ba_status, -+ sequence, 0, NULL); -+ goto out; -+ } -+ } -+ -+ } -+ /* Require authorization option for RO, home reg is protected by IPsec */ -+ if (!(flags & MIPV6_BU_F_HOME) && !auth) { -+ MIPV6_INC_STATS(n_bu_drop.auth); -+ if (key_bu) -+ kfree(key_bu); -+ return MH_AUTH_FAILED; -+ } -+ -+ if (mipv6_bcache_get(haddr, dst, &bc_entry) == 0) { -+ if ((bc_entry.flags&MIPV6_BU_F_HOME) != -+ (flags&MIPV6_BU_F_HOME)) { -+ DEBUG(DBG_INFO, -+ "Registration type change. Sending BA REG_TYPE_CHANGE_FORBIDDEN"); -+ mipv6_send_ba(dst, haddr, coa, reply_coa, -+ REG_TYPE_CHANGE_FORBIDDEN, -+ sequence, lifetime, key_bu); -+ goto out; -+ } -+ if (!MIPV6_SEQ_GT(sequence, bc_entry.seq)) { -+ DEBUG(DBG_INFO, -+ "Sequence number mismatch. Sending BA SEQUENCE_NUMBER_OUT_OF_WINDOW"); -+ mipv6_send_ba(dst, haddr, coa, reply_coa, -+ SEQUENCE_NUMBER_OUT_OF_WINDOW, -+ bc_entry.seq, lifetime, key_bu); -+ goto out; -+ } -+ } -+ -+ if (!dereg) { -+ int ifindex; -+ struct rt6_info *rt; -+ -+ /* Avoid looping binding cache entries */ -+ if (mipv6_bcache_get(coa, dst, &bc_entry) == 0) { -+ DEBUG(DBG_WARNING, "Looped BU, dropping the packet"); -+ goto out; -+ } -+ DEBUG(DBG_INFO, "calling bu_add."); -+ if ((rt = rt6_lookup(haddr, dst, 0, 0)) != NULL) { -+ ifindex = rt->rt6i_dev->ifindex; -+ dst_release(&rt->u.dst); -+ } else { -+ /* -+ * Can't process the BU since the right interface is -+ * not found. -+ */ -+ DEBUG(DBG_WARNING, "No route entry found for handling " -+ "a BU request, (using 0 as index)"); -+ ifindex = 0; -+ } -+ if (flags & MIPV6_BU_F_HOME) -+ mip6_fn.bce_home_add(ifindex, dst, haddr, coa, -+ reply_coa, lifetime, sequence, -+ flags, key_bu); -+ else -+ mip6_fn.bce_cache_add(ifindex, dst, haddr, coa, -+ reply_coa, lifetime, sequence, -+ flags, key_bu); -+ } else { -+ DEBUG(DBG_INFO, "calling BCE delete."); -+ -+ if (flags & MIPV6_BU_F_HOME) -+ mip6_fn.bce_home_del(dst, haddr, coa, reply_coa, -+ sequence, flags, key_bu); -+ else { -+ mipv6_rr_invalidate_nonce(nonce_ind); -+ mip6_fn.bce_cache_del(dst, haddr, coa, reply_coa, -+ sequence, flags, key_bu); -+ } -+ } -+ out: -+ MIPV6_INC_STATS(n_bu_rcvd); -+ if (key_bu) -+ kfree(key_bu); -+ return 0; -+} -+ -+static int mipv6_mh_rcv(struct sk_buff *skb) -+{ -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; -+ struct mipv6_mh *mh; -+ struct in6_addr *lhome, *fhome, *lcoa = NULL, *fcoa = NULL; -+ int ret = 0; -+ -+ fhome = &skb->nh.ipv6h->saddr; -+ lhome = &skb->nh.ipv6h->daddr; -+ -+ if (opt->hao != 0) { -+ struct mipv6_dstopt_homeaddr *hao; -+ hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao); -+ fcoa = &hao->addr; -+ } -+ -+ if (opt->srcrt2 != 0) { -+ struct rt2_hdr *rt2; -+ rt2 = (struct rt2_hdr *)((u8 *)skb->nh.raw + opt->srcrt2); -+ lcoa = &rt2->addr; -+ } -+ -+ /* Verify checksum is correct */ -+ if (skb->ip_summed == CHECKSUM_HW) { -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY, -+ skb->csum)) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING "MIPv6 MH hw checksum failed\n"); -+ skb->ip_summed = CHECKSUM_NONE; -+ } -+ } -+ if (skb->ip_summed == CHECKSUM_NONE) { -+ if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY, -+ skb_checksum(skb, 0, skb->len, 0))) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING "MIPv6 MH checksum failed\n"); -+ goto bad; -+ } -+ } -+ -+ if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*mh)) || -+ !pskb_may_pull(skb, -+ skb->h.raw-skb->data+((skb->h.raw[1]+1)<<3))) { -+ DEBUG(DBG_INFO, "MIPv6 MH invalid length"); -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ mh = (struct mipv6_mh *) skb->h.raw; -+ -+ /* Verify there are no more headers after the MH */ -+ if (mh->payload != NEXTHDR_NONE) { -+ __u32 pos = (__u32)&mh->payload - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "MIPv6 MH error"); -+ goto bad; -+ } -+ -+ if (mh->type > MIPV6_MH_MAX) { -+ /* send binding error */ -+ printk("Invalid mobility header type (%d)\n", mh->type); -+ mipv6_send_be(lhome, fcoa ? fcoa : fhome, -+ fcoa ? fhome : NULL, -+ MIPV6_BE_UNKNOWN_MH_TYPE); -+ goto bad; -+ } -+ if (mh_rcv[mh->type].func != NULL) { -+ ret = mh_rcv[mh->type].func(skb, lhome, lcoa, fhome, fcoa, mh); -+ } else { -+ DEBUG(DBG_INFO, "No handler for MH Type %d", mh->type); -+ goto bad; -+ } -+ -+ kfree_skb(skb); -+ return 0; -+ -+bad: -+ MIPV6_INC_STATS(n_mh_in_error); -+ kfree_skb(skb); -+ return 0; -+ -+} -+ -+#if LINUX_VERSION_CODE >= 0x2052a -+struct inet6_protocol mipv6_mh_protocol = -+{ -+ mipv6_mh_rcv, /* handler */ -+ NULL /* error control */ -+}; -+#else -+struct inet6_protocol mipv6_mh_protocol = -+{ -+ mipv6_mh_rcv, /* handler */ -+ NULL, /* error control */ -+ NULL, /* next */ -+ IPPROTO_MOBILITY, /* protocol ID */ -+ 0, /* copy */ -+ NULL, /* data */ -+ "MIPv6 MH" /* name */ -+}; -+#endif -+ -+/* -+ * -+ * Code module init/exit functions -+ * -+ */ -+ -+int __init mipv6_mh_common_init(void) -+{ -+ struct sock *sk; -+ int err; -+ -+ mip6_fn.bce_home_add = bc_cn_home_add; -+ mip6_fn.bce_cache_add = bc_cache_add; -+ mip6_fn.bce_home_del = bc_cn_home_delete; -+ mip6_fn.bce_cache_del = bc_cache_delete; -+ -+ mipv6_mh_socket = sock_alloc(); -+ if (mipv6_mh_socket == NULL) { -+ printk(KERN_ERR -+ "Failed to create the MIP6 MH control socket.\n"); -+ return -1; -+ } -+ mipv6_mh_socket->type = SOCK_RAW; -+ -+ if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_MOBILITY, -+ &mipv6_mh_socket)) < 0) { -+ printk(KERN_ERR -+ "Failed to initialize the MIP6 MH control socket (err %d).\n", -+ err); -+ sock_release(mipv6_mh_socket); -+ mipv6_mh_socket = NULL; /* for safety */ -+ return err; -+ } -+ -+ sk = mipv6_mh_socket->sk; -+ sk->allocation = GFP_ATOMIC; -+ sk->sndbuf = 64 * 1024 + sizeof(struct sk_buff); -+ sk->prot->unhash(sk); -+ -+ memset(&mh_rcv, 0, sizeof(mh_rcv)); -+ mh_rcv[MIPV6_MH_HOTI].func = mipv6_handle_mh_testinit; -+ mh_rcv[MIPV6_MH_COTI].func = mipv6_handle_mh_testinit; -+ mh_rcv[MIPV6_MH_BU].func = mipv6_handle_mh_bu; -+ -+#if LINUX_VERSION_CODE >= 0x2052a -+ if (inet6_add_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY) < 0) { -+ printk(KERN_ERR "Failed to register MOBILITY protocol\n"); -+ sock_release(mipv6_mh_socket); -+ mipv6_mh_socket = NULL; -+ return -EAGAIN; -+ } -+#else -+ inet6_add_protocol(&mipv6_mh_protocol); -+#endif -+ /* To disable the use of dst_cache, -+ * which slows down the sending of BUs ?? -+ */ -+ sk->dst_cache=NULL; -+ -+ return 0; -+} -+ -+void __exit mipv6_mh_common_exit(void) -+{ -+ if (mipv6_mh_socket) sock_release(mipv6_mh_socket); -+ mipv6_mh_socket = NULL; /* For safety. */ -+ -+#if LINUX_VERSION_CODE >= 0x2052a -+ inet6_del_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY); -+#else -+ inet6_del_protocol(&mipv6_mh_protocol); -+#endif -+ memset(&mh_rcv, 0, sizeof(mh_rcv)); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_mn.c linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/mobhdr_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/mobhdr_mn.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,1155 @@ -+/* -+ * Mobile IPv6 Mobility Header Functions for Mobile Node -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * Niklas Kämpe <nhkampe@cc.hut.fi> -+ * Henrik Petander <henrik.petander@hut.fi> -+ * -+ * $Id:$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/types.h> -+#include <linux/sched.h> -+#include <linux/init.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "mobhdr.h" -+#include "mn.h" -+#include "bul.h" -+#include "rr_crypto.h" -+#include "debug.h" -+#include "util.h" -+#include "stats.h" -+ -+int rr_configured = 1; -+ -+/* Return value of mipv6_rr_state() */ -+#define NO_RR 0 -+#define DO_RR 1 -+#define RR_FOR_COA 2 -+#define INPROGRESS_RR 3 -+ -+/** -+ * send_bu_msg - sends a Binding Update -+ * @bulentry : BUL entry with the information for building a BU -+ * -+ * Function builds a BU msg based on the contents of a bul entry. -+ * Does not change the bul entry. -+ **/ -+static int send_bu_msg(struct mipv6_bul_entry *binding) -+{ -+ int auth = 0; /* Use auth */ -+ int ret = 0; -+ struct mipv6_auth_parm parm; -+ struct mipv6_mh_bu bu; -+ -+ if (!binding) { -+ DEBUG(DBG_ERROR, "called with a null bul entry"); -+ return -1; -+ } -+ -+ memset(&parm, 0, sizeof(parm)); -+ if (mipv6_prefix_compare(&binding->coa, &binding->home_addr, 64)) -+ parm.coa = &binding->home_addr; -+ else -+ parm.coa = &binding->coa; -+ parm.cn_addr = &binding->cn_addr; -+ -+ if (binding->rr && binding->rr->kbu) { -+ DEBUG(DBG_INFO, "Binding with key"); -+ auth = 1; -+ parm.k_bu = binding->rr->kbu; -+ } -+ memset(&bu, 0, sizeof(bu)); -+ bu.flags = binding->flags; -+ bu.sequence = htons(binding->seq); -+ bu.lifetime = htons(binding->lifetime >> 2); -+ bu.reserved = 0; -+ -+ ret = send_mh(&binding->cn_addr, &binding->home_addr, -+ MIPV6_MH_BU, sizeof(bu), (u8 *)&bu, -+ &binding->home_addr, NULL, -+ binding->ops, &parm); -+ -+ if (ret == 0) -+ MIPV6_INC_STATS(n_bu_sent); -+ -+ return ret; -+} -+ -+/** -+ * mipv6_send_addr_test_init - send a HoTI or CoTI message -+ * @saddr: source address for H/CoTI -+ * @daddr: destination address for H/CoTI -+ * @msg_type: Identifies whether HoTI or CoTI -+ * @init_cookie: the HoTi or CoTi init cookie -+ * -+ * The message will be retransmitted till we get a HoT or CoT message, since -+ * our caller (mipv6_RR_start) has entered this message in the BUL with -+ * exponential backoff retramission set. -+ */ -+static int mipv6_send_addr_test_init(struct in6_addr *saddr, -+ struct in6_addr *daddr, -+ u8 msg_type, -+ u8 *init_cookie) -+{ -+ struct mipv6_mh_addr_ti ti; -+ struct mipv6_mh_opt *ops = NULL; -+ int ret = 0; -+ -+ /* Set reserved and copy the cookie from address test init msg */ -+ ti.reserved = 0; -+ mipv6_rr_mn_cookie_create(init_cookie); -+ memcpy(ti.init_cookie, init_cookie, MIPV6_RR_COOKIE_LENGTH); -+ -+ ret = send_mh(daddr, saddr, msg_type, sizeof(ti), (u8 *)&ti, -+ NULL, NULL, ops, NULL); -+ if (ret == 0) { -+ if (msg_type == MIPV6_MH_HOTI) { -+ MIPV6_INC_STATS(n_hoti_sent); -+ } else { -+ MIPV6_INC_STATS(n_coti_sent); -+ } -+ } -+ -+ return ret; -+} -+ -+/* -+ * -+ * Callback handlers for binding update list -+ * -+ */ -+ -+/* Return value 0 means keep entry, non-zero means discard entry. */ -+ -+/* Callback for BUs not requiring acknowledgement -+ */ -+int bul_entry_expired(struct mipv6_bul_entry *bulentry) -+{ -+ /* Lifetime expired, delete entry. */ -+ DEBUG(DBG_INFO, "bul entry 0x%p lifetime expired, deleting entry", -+ bulentry); -+ return 1; -+} -+ -+/* Callback for BUs requiring acknowledgement with exponential resending -+ * scheme */ -+static int bul_resend_exp(struct mipv6_bul_entry *bulentry) -+{ -+ unsigned long now = jiffies; -+ -+ DEBUG(DBG_INFO, "(0x%x) resending bu", (int) bulentry); -+ -+ -+ /* If sending a de-registration, do not care about the -+ * lifetime value, as de-registrations are normally sent with -+ * a zero lifetime value. If the entry is a home entry get the -+ * current lifetime. -+ */ -+ -+ if (bulentry->lifetime != 0) { -+ bulentry->lifetime = mipv6_mn_get_bulifetime( -+ &bulentry->home_addr, &bulentry->coa, bulentry->flags); -+ -+ bulentry->expire = now + bulentry->lifetime * HZ; -+ } else { -+ bulentry->expire = now + HOME_RESEND_EXPIRE * HZ; -+ } -+ if (bulentry->rr) { -+ /* Redo RR, if cookies have expired */ -+ if (time_after(jiffies, bulentry->rr->home_time + MAX_TOKEN_LIFE * HZ)) -+ bulentry->rr->rr_state |= RR_WAITH; -+ if (time_after(jiffies, bulentry->rr->careof_time + MAX_NONCE_LIFE * HZ)) -+ bulentry->rr->rr_state |= RR_WAITC; -+ -+ if (bulentry->rr->rr_state & RR_WAITH) { -+ /* Resend HoTI directly */ -+ mipv6_send_addr_test_init(&bulentry->home_addr, -+ &bulentry->cn_addr, MIPV6_MH_HOTI, -+ bulentry->rr->hot_cookie); -+ } -+ if (bulentry->rr->rr_state & RR_WAITC) { -+ /* Resend CoTI directly */ -+ mipv6_send_addr_test_init(&bulentry->coa, -+ &bulentry->cn_addr, MIPV6_MH_COTI, -+ bulentry->rr->cot_cookie); -+ } -+ goto out; -+ } -+ -+ bulentry->seq++; -+ -+ if (send_bu_msg(bulentry) < 0) -+ DEBUG(DBG_ERROR, "Resending of BU failed"); -+ -+out: -+ /* Schedule next retransmission */ -+ if (bulentry->delay < bulentry->maxdelay) { -+ bulentry->delay = 2 * bulentry->delay; -+ if (bulentry->delay > bulentry->maxdelay) { -+ /* can happen if maxdelay is not power(mindelay, 2) */ -+ bulentry->delay = bulentry->maxdelay; -+ } -+ } else if (bulentry->flags & MIPV6_BU_F_HOME) { -+ /* Home registration - continue sending BU at maxdelay rate */ -+ DEBUG(DBG_INFO, "Sending BU to HA after max ack wait time " -+ "reached(0x%x)", (int) bulentry); -+ bulentry->delay = bulentry->maxdelay; -+ } else if (!(bulentry->flags & MIPV6_BU_F_HOME)) { -+ /* Failed to get BA from a CN */ -+ bulentry->callback_time = now; -+ return -1; -+ } -+ -+ bulentry->callback_time = now + bulentry->delay * HZ; -+ return 0; -+} -+ -+ -+ -+/* Callback for sending a registration refresh BU -+ */ -+static int bul_refresh(struct mipv6_bul_entry *bulentry) -+{ -+ unsigned long now = jiffies; -+ -+ /* Refresh interval passed, send new BU */ -+ DEBUG(DBG_INFO, "bul entry 0x%x refresh interval passed, sending new BU", (int) bulentry); -+ if (bulentry->lifetime == 0) -+ return 0; -+ -+ /* Set new maximum lifetime and expiration time */ -+ bulentry->lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, -+ &bulentry->coa, -+ bulentry->flags); -+ bulentry->expire = now + bulentry->lifetime * HZ; -+ bulentry->seq++; -+ /* Send update */ -+ if (send_bu_msg(bulentry) < 0) -+ DEBUG(DBG_ERROR, "Resending of BU failed"); -+ -+ if (time_after_eq(now, bulentry->expire)) { -+ /* Sanity check */ -+ DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs", ERROR_DEF_LIFETIME); -+ bulentry->lifetime = ERROR_DEF_LIFETIME; -+ bulentry->expire = now + ERROR_DEF_LIFETIME*HZ; -+ } -+ -+ /* Set up retransmission */ -+ bulentry->state = RESEND_EXP; -+ bulentry->callback = bul_resend_exp; -+ bulentry->callback_time = now + INITIAL_BINDACK_TIMEOUT*HZ; -+ bulentry->delay = INITIAL_BINDACK_TIMEOUT; -+ bulentry->maxdelay = MAX_BINDACK_TIMEOUT; -+ -+ return 0; -+} -+ -+static int mipv6_send_RR_bu(struct mipv6_bul_entry *bulentry) -+{ -+ int ret; -+ int ops_len = 0; -+ u16 nonces[2]; -+ -+ DEBUG(DBG_INFO, "Sending BU to CN %x:%x:%x:%x:%x:%x:%x:%x " -+ "for home address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&bulentry->cn_addr), NIPV6ADDR(&bulentry->home_addr)); -+ nonces[0] = bulentry->rr->home_nonce_index; -+ nonces[1] = bulentry->rr->careof_nonce_index; -+ ops_len = sizeof(struct mipv6_mo_bauth_data) + MIPV6_RR_MAC_LENGTH + -+ sizeof(struct mipv6_mo_nonce_indices); -+ if (bulentry->ops) { -+ DEBUG(DBG_WARNING, "Bul entry had existing mobility options, freeing them"); -+ kfree(bulentry->ops); -+ } -+ bulentry->ops = alloc_mh_opts(ops_len); -+ -+ if (!bulentry->ops) -+ return -ENOMEM; -+ if (append_mh_opt(bulentry->ops, MIPV6_OPT_NONCE_INDICES, -+ sizeof(struct mipv6_mo_nonce_indices) - 2, nonces) < 0) -+ return -ENOMEM; -+ -+ if (append_mh_opt(bulentry->ops, MIPV6_OPT_AUTH_DATA, -+ MIPV6_RR_MAC_LENGTH, NULL) < 0) -+ return -ENOMEM; -+ /* RR procedure is over, send a BU */ -+ if (!(bulentry->flags & MIPV6_BU_F_ACK)) { -+ DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired"); -+ bulentry->state = ACK_OK; -+ bulentry->callback = bul_entry_expired; -+ bulentry->callback_time = jiffies + HZ * bulentry->lifetime; -+ bulentry->expire = jiffies + HZ * bulentry->lifetime; -+ } -+ else { -+ bulentry->callback_time = jiffies + HZ; -+ bulentry->expire = jiffies + HZ * bulentry->lifetime; -+ } -+ -+ ret = send_bu_msg(bulentry); -+ mipv6_bul_reschedule(bulentry); -+ return ret; -+} -+ -+static int mipv6_rr_state(struct mipv6_bul_entry *bul, struct in6_addr *saddr, -+ struct in6_addr *coa, __u8 flags) -+{ -+ if (!rr_configured) -+ return NO_RR; -+ if (flags & MIPV6_BU_F_HOME) { -+ /* We don't need RR, this is a Home Registration */ -+ return NO_RR; -+ } -+ if (!bul || !bul->rr) { -+ /* First time BU to CN, need RR */ -+ return DO_RR; -+ } -+ -+ switch (bul->rr->rr_state) { -+ case RR_INIT: -+ /* Need RR if first BU to CN */ -+ return DO_RR; -+ case RR_DONE: -+ /* If MN moves to a new coa, do RR for it */ -+ if (!ipv6_addr_cmp(&bul->coa, coa)) -+ return NO_RR; -+ else -+ return DO_RR; -+ default: -+ /* -+ * We are in the middle of RR, the HoTI and CoTI have been -+ * sent. But we haven't got HoT and CoT from the CN, so -+ * don't do anything more at this time. -+ */ -+ return INPROGRESS_RR; -+ } -+} -+ -+/** -+ * mipv6_RR_start - Start Return Routability procedure -+ * @home_addr: home address -+ * @cn_addr: correspondent address -+ * @coa: care-of address -+ * @entry: binding update list entry (if any) -+ * @initdelay: initial ack timeout -+ * @maxackdelay: maximum ack timeout -+ * @flags: flags -+ * @lifetime: lifetime of binding -+ * @ops: mobility options -+ * -+ * Caller must hold @bul_lock (write). -+ **/ -+static int mipv6_RR_start(struct in6_addr *home_addr, struct in6_addr *cn_addr, -+ struct in6_addr *coa, struct mipv6_bul_entry *entry, -+ __u32 initdelay, __u32 maxackdelay, __u8 flags, -+ __u32 lifetime, struct mipv6_mh_opt *ops) -+{ -+ int ret = -1; -+ struct mipv6_bul_entry *bulentry = entry; -+ struct mipv6_rr_info *rr = NULL; -+ int seq = 0; -+ DEBUG_FUNC(); -+ -+ /* Do RR procedure only for care-of address after handoff, -+ if home cookie is still valid */ -+ if (bulentry && bulentry->rr) { -+ if (time_before(jiffies, bulentry->rr->home_time + MAX_NONCE_LIFE * HZ) && -+ lifetime && !(ipv6_addr_cmp(home_addr, coa) == 0)) { -+ mipv6_rr_mn_cookie_create(bulentry->rr->cot_cookie); -+ DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for CoA"); -+ ipv6_addr_copy(&bulentry->coa, coa); -+ bulentry->rr->rr_state |= RR_WAITC; -+ } else if (!lifetime) { /* Send only HoTi when returning home */ -+ mipv6_rr_mn_cookie_create(bulentry->rr->hot_cookie); -+ DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for HoA"); -+ ipv6_addr_copy(&bulentry->coa, coa); /* Home address as CoA */ -+ bulentry->rr->rr_state |= RR_WAITH; -+ } -+ } else { -+ DEBUG(DBG_INFO, "Doing RR for both HoA and CoA"); -+ rr = kmalloc(sizeof(*rr), GFP_ATOMIC); -+ memset(rr, 0, sizeof(*rr)); -+ rr->rr_state = RR_WAITHC; -+ } -+ if (bulentry) { -+ if (bulentry->state == ACK_ERROR) -+ goto out; -+ seq = bulentry->seq + 1; -+ } else -+ seq = 0; -+ /* Save the info in the BUL to retransmit the BU after RR is done */ -+ /* Caller must hold bul_lock (write) since we don't */ -+ -+ if ((bulentry = mipv6_bul_add(cn_addr, home_addr, coa, -+ min_t(__u32, lifetime, MAX_RR_BINDING_LIFE), -+ seq, flags, bul_resend_exp, initdelay, -+ RESEND_EXP, initdelay, -+ maxackdelay, ops, -+ rr)) == NULL) { -+ DEBUG(DBG_INFO, "couldn't update BUL for HoTi"); -+ goto out; -+ } -+ -+ rr = bulentry->rr; -+ if (rr->rr_state&RR_WAITH) -+ mipv6_send_addr_test_init(home_addr, cn_addr, MIPV6_MH_HOTI, -+ rr->hot_cookie); -+ if (ipv6_addr_cmp(home_addr, coa) && lifetime) -+ mipv6_send_addr_test_init(coa, cn_addr, MIPV6_MH_COTI, rr->cot_cookie); -+ else { -+ bulentry->rr->rr_state &= ~RR_WAITC; -+ } -+ ret = 0; -+out: -+ return ret; -+} -+ -+/* -+ * Status codes for mipv6_ba_rcvd() -+ */ -+#define STATUS_UPDATE 0 -+#define STATUS_REMOVE 1 -+ -+/** -+ * mipv6_ba_rcvd - Update BUL for this Binding Acknowledgement -+ * @ifindex: interface BA came from -+ * @cnaddr: sender IPv6 address -+ * @home_addr: home address -+ * @sequence: sequence number -+ * @lifetime: lifetime granted by Home Agent in seconds -+ * @refresh: recommended resend interval -+ * @status: %STATUS_UPDATE (ack) or %STATUS_REMOVE (nack) -+ * -+ * This function must be called to notify the module of the receipt of -+ * a binding acknowledgement so that it can cease retransmitting the -+ * option. The caller must have validated the acknowledgement before calling -+ * this function. 'status' can be either STATUS_UPDATE in which case the -+ * binding acknowledgement is assumed to be valid and the corresponding -+ * binding update list entry is updated, or STATUS_REMOVE in which case -+ * the corresponding binding update list entry is removed (this can be -+ * used upon receiving a negative acknowledgement). -+ * Returns 0 if a matching binding update has been sent or non-zero if -+ * not. -+ */ -+static int mipv6_ba_rcvd(int ifindex, struct in6_addr *cnaddr, -+ struct in6_addr *home_addr, -+ u16 sequence, u32 lifetime, -+ u32 refresh, int status) -+{ -+ struct mipv6_bul_entry *bulentry; -+ unsigned long now = jiffies; -+ struct in6_addr coa; -+ -+ DEBUG(DBG_INFO, "BA received with sequence number 0x%x, status: %d", -+ (int) sequence, status); -+ -+ /* Find corresponding entry in binding update list. */ -+ write_lock(&bul_lock); -+ if ((bulentry = mipv6_bul_get(cnaddr, home_addr)) == NULL) { -+ DEBUG(DBG_INFO, "- discarded, no entry in bul matches BA source address"); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ -+ ipv6_addr_copy(&coa, &bulentry->coa); -+ if (status == SEQUENCE_NUMBER_OUT_OF_WINDOW) { -+ __u32 lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, -+ &bulentry->coa, -+ bulentry->flags); -+ bulentry->seq = sequence; -+ -+ mipv6_send_bu(&bulentry->home_addr, &bulentry->cn_addr, -+ &bulentry->coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, bulentry->flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ return 0; -+ } else if (status >= REASON_UNSPECIFIED) { -+ int err; -+ int at_home = MN_NOT_AT_HOME; -+ DEBUG(DBG_WARNING, "- NACK - BA status: %d, deleting bul entry", status); -+ if (bulentry->flags & MIPV6_BU_F_HOME) { -+ struct mn_info *minfo; -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_home(home_addr); -+ if (minfo) { -+ spin_lock(&minfo->lock); -+ if (minfo->is_at_home != MN_NOT_AT_HOME) -+ minfo->is_at_home = MN_AT_HOME; -+ at_home = minfo->is_at_home; -+ minfo->has_home_reg = 0; -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock(&mn_info_lock); -+ DEBUG(DBG_ERROR, "Home registration failed: BA status: %d, deleting bul entry", status); -+ } -+ write_unlock(&bul_lock); -+ err = mipv6_bul_delete(cnaddr, home_addr); -+ if (at_home == MN_AT_HOME) { -+ mipv6_mn_send_home_na(home_addr); -+ write_lock_bh(&bul_lock); -+ mipv6_bul_iterate(mn_cn_handoff, &coa); -+ write_unlock_bh(&bul_lock); -+ } -+ return err; -+ } -+ bulentry->state = ACK_OK; -+ -+ if (bulentry->flags & MIPV6_BU_F_HOME && lifetime > 0) { -+ /* For home registrations: schedule a refresh binding update. -+ * Use the refresh interval given by home agent or 80% -+ * of lifetime, whichever is less. -+ * -+ * Adjust binding lifetime if 'granted' lifetime -+ * (lifetime value in received binding acknowledgement) -+ * is shorter than 'requested' lifetime (lifetime -+ * value sent in corresponding binding update). -+ * max((L_remain - (L_update - L_ack)), 0) -+ */ -+ if (lifetime * HZ < (bulentry->expire - bulentry->lastsend)) { -+ bulentry->expire = -+ max_t(__u32, bulentry->expire - -+ ((bulentry->expire - bulentry->lastsend) - -+ lifetime * HZ), jiffies + -+ ERROR_DEF_LIFETIME * HZ); -+ } -+ if (refresh > lifetime || refresh == 0) -+ refresh = 4 * lifetime / 5; -+ DEBUG(DBG_INFO, "setting callback for expiration of" -+ " a Home Registration: lifetime:%d, refresh:%d", -+ lifetime, refresh); -+ bulentry->callback = bul_refresh; -+ bulentry->callback_time = now + refresh * HZ; -+ bulentry->expire = now + lifetime * HZ; -+ bulentry->lifetime = lifetime; -+ if (time_after_eq(jiffies, bulentry->expire)) { -+ /* Sanity check */ -+ DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs", -+ ERROR_DEF_LIFETIME); -+ bulentry->expire = jiffies + ERROR_DEF_LIFETIME * HZ; -+ } -+ mipv6_mn_set_home_reg(home_addr, 1); -+ mipv6_bul_iterate(mn_cn_handoff, &coa); -+ } else if ((bulentry->flags & MIPV6_BU_F_HOME) && bulentry->lifetime == 0) { -+ write_unlock(&bul_lock); -+ DEBUG(DBG_INFO, "Got BA for deregistration BU"); -+ mipv6_mn_set_home_reg(home_addr, 0); -+ mipv6_bul_delete(cnaddr, home_addr); -+ mipv6_mn_send_home_na(home_addr); -+ -+ write_lock_bh(&bul_lock); -+ mipv6_bul_iterate(mn_cn_handoff, &coa); -+ write_unlock_bh(&bul_lock); -+ return 0; -+ } -+ -+ mipv6_bul_reschedule(bulentry); -+ write_unlock(&bul_lock); -+ -+ return 0; -+} -+ -+static int mipv6_handle_mh_HC_test(struct sk_buff *skb, -+ struct in6_addr *saddr, -+ struct in6_addr *fcoa, -+ struct in6_addr *cn, -+ struct in6_addr *lcoa, -+ struct mipv6_mh *mh) -+{ -+ int ret = 0; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ -+ struct mipv6_mh_addr_test *tm = (struct mipv6_mh_addr_test *)mh->data; -+ struct mipv6_bul_entry *bulentry; -+ -+ DEBUG_FUNC(); -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*tm); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "Mobility Header length less than H/C Test"); -+ return -1; -+ } -+ if (fcoa || lcoa) { -+ DEBUG(DBG_INFO, "H/C Test has HAO or RTH2, dropped."); -+ return -1; -+ } -+ write_lock(&bul_lock); -+ -+ /* We need to get the home address, since CoT only has the CoA*/ -+ if (mh->type == MIPV6_MH_COT) { -+ if ((bulentry = mipv6_bul_get_by_ccookie(cn, tm->init_cookie)) == NULL) { -+ DEBUG(DBG_ERROR, "has no BUL or RR state for " -+ "source:%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(cn)); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ } else { /* HoT has the home address */ -+ if (((bulentry = mipv6_bul_get(cn, saddr)) == NULL) || !bulentry->rr) { -+ DEBUG(DBG_ERROR, "has no BUL or RR state for " -+ "source:%x:%x:%x:%x:%x:%x:%x:%x " -+ "dest:%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(cn), NIPV6ADDR(saddr)); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ } -+ -+ switch (mh->type) { -+ case MIPV6_MH_HOT: -+ if ((bulentry->rr->rr_state & RR_WAITH) == 0) { -+ DEBUG(DBG_ERROR, "Not waiting for a Home Test message"); -+ goto out; -+ } -+ /* -+ * Make sure no home cookies have been received yet. -+ * TODO: Check not being put in at this time since subsequent -+ * BU's after this time will have home cookie stored. -+ */ -+ -+ /* Check if the cookie received is the right one */ -+ if (!mipv6_equal_cookies(tm->init_cookie, -+ bulentry->rr->hot_cookie)) { -+ /* Invalid cookie, might be an old cookie */ -+ DEBUG(DBG_WARNING, "Received HoT cookie does not match stored cookie"); -+ goto out; -+ } -+ DEBUG(DBG_INFO, "Got Care-of Test message"); -+ bulentry->rr->rr_state &= ~RR_WAITH; -+ memcpy(bulentry->rr->home_cookie, tm->kgen_token, MIPV6_COOKIE_LEN); -+ bulentry->rr->home_nonce_index = tm->nonce_index; -+ bulentry->rr->home_time = jiffies; -+ ret = 1; -+ break; -+ -+ case MIPV6_MH_COT: -+ if ((bulentry->rr->rr_state & RR_WAITC) == 0) { -+ DEBUG(DBG_ERROR, "Not waiting for a Home Test message"); -+ goto out; -+ } -+ /* -+ * Make sure no home cookies have been received yet. -+ * TODO: Check not being put in at this time since subsequent -+ * BU's at this time will have careof cookie stored. -+ */ -+ -+ /* Check if the cookie received is the right one */ -+ if (!mipv6_equal_cookies(tm->init_cookie, -+ bulentry->rr->cot_cookie)) { -+ DEBUG(DBG_INFO, "Received CoT cookie does not match stored cookie"); -+ goto out; -+ } -+ bulentry->rr->rr_state &= ~RR_WAITC; -+ memcpy(bulentry->rr->careof_cookie, tm->kgen_token, MIPV6_COOKIE_LEN); -+ bulentry->rr->careof_nonce_index = tm->nonce_index; -+ bulentry->rr->careof_time = jiffies; -+ ret = 1; -+ break; -+ default: -+ /* Impossible to get here */ -+ break; -+ } -+out: -+ if (bulentry->rr->rr_state == RR_DONE) { -+ if (bulentry->rr->kbu) /* First free any old keys */ -+ kfree(bulentry->rr->kbu); -+ /* Store the session key to be used in BU's */ -+ if (ipv6_addr_cmp(&bulentry->coa, &bulentry->home_addr) && bulentry->lifetime) -+ bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie, -+ bulentry->rr->careof_cookie); -+ else -+ bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie, -+ NULL); -+ /* RR procedure is over, send a BU */ -+ mipv6_send_RR_bu(bulentry); -+ } -+ write_unlock(&bul_lock); -+ return ret; -+} -+ -+/** -+ * mipv6_handle_mh_brr - Binding Refresh Request handler -+ * @home: home address -+ * @coa: care-of address -+ * @cn: source of this packet -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ * Handles Binding Refresh Request. Packet and offset to option are -+ * passed. Returns 0 on success, otherwise negative. -+ **/ -+static int mipv6_handle_mh_brr(struct sk_buff *skb, -+ struct in6_addr *home, -+ struct in6_addr *unused1, -+ struct in6_addr *cn, -+ struct in6_addr *unused2, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_brr *brr = (struct mipv6_mh_brr *)mh->data; -+ struct mipv6_bul_entry *binding; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*brr); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_WARNING, "Mobility Header length less than BRR"); -+ MIPV6_INC_STATS(n_brr_drop.invalid); -+ return -1; -+ } -+ -+ /* check we know src, else drop */ -+ write_lock(&bul_lock); -+ if ((binding = mipv6_bul_get(cn, home)) == NULL) { -+ MIPV6_INC_STATS(n_brr_drop.misc); -+ write_unlock(&bul_lock); -+ return MH_UNKNOWN_CN; -+ } -+ -+ MIPV6_INC_STATS(n_brr_rcvd); -+ -+ if (opt_len > 0) { -+ struct mobopt opts; -+ memset(&opts, 0, sizeof(opts)); -+ if (parse_mo_tlv(brr + 1, opt_len, &opts) < 0) { -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ /* -+ * MIPV6_OPT_AUTH_DATA -+ */ -+ } -+ -+ /* must hold bul_lock (write) */ -+ mipv6_RR_start(home, cn, &binding->coa, binding, binding->delay, -+ binding->maxdelay, binding->flags, -+ binding->lifetime, binding->ops); -+ -+ write_unlock(&bul_lock); -+ /* MAY also decide to delete binding and send zero lifetime BU -+ with alt-coa set to home address */ -+ -+ return 0; -+} -+ -+/** -+ * mipv6_handle_mh_ba - Binding Acknowledgement handler -+ * @src: source of this packet -+ * @coa: care-of address -+ * @home: home address -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ **/ -+static int mipv6_handle_mh_ba(struct sk_buff *skb, -+ struct in6_addr *home, -+ struct in6_addr *coa, -+ struct in6_addr *src, -+ struct in6_addr *unused, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_ba *ba = (struct mipv6_mh_ba *)mh->data; -+ struct mipv6_bul_entry *binding = NULL; -+ struct mobopt opts; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ -+ int auth = 1, req_auth = 1, refresh = -1, ifindex = 0; -+ u32 lifetime, sequence; -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*ba); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_WARNING, "Mobility Header length less than BA"); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ return -1; -+ } -+ -+ lifetime = ntohs(ba->lifetime) << 2; -+ sequence = ntohs(ba->sequence); -+ -+ if (opt_len > 0) { -+ memset(&opts, 0, sizeof(opts)); -+ if (parse_mo_tlv(ba + 1, opt_len, &opts) < 0) -+ return -1; -+ /* -+ * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_BR_ADVICE -+ */ -+ if (opts.br_advice) -+ refresh = ntohs(opts.br_advice->refresh_interval); -+ } -+ -+ if (ba->status >= EXPIRED_HOME_NONCE_INDEX && -+ ba->status <= EXPIRED_NONCES) -+ req_auth = 0; -+ -+ write_lock(&bul_lock); -+ binding = mipv6_bul_get(src, home); -+ if (!binding) { -+ DEBUG(DBG_INFO, "No binding, BA dropped."); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ -+ if (opts.auth_data && binding->rr && -+ (mipv6_auth_check(src, coa, (__u8 *)mh, msg_len, -+ opts.auth_data, binding->rr->kbu) == 0)) -+ auth = 1; -+ -+ if (req_auth && binding->rr && !auth) { -+ DEBUG(DBG_INFO, "BA Authentication failed."); -+ MIPV6_INC_STATS(n_ba_drop.auth); -+ write_unlock(&bul_lock); -+ return MH_AUTH_FAILED; -+ } -+ -+ if (ba->status == SEQUENCE_NUMBER_OUT_OF_WINDOW) { -+ DEBUG(DBG_INFO, -+ "Sequence number out of window, setting seq to %d", -+ sequence); -+ } else if (binding->seq != sequence) { -+ DEBUG(DBG_INFO, "BU/BA Sequence Number mismatch %d != %d", -+ binding->seq, sequence); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ write_unlock(&bul_lock); -+ return MH_SEQUENCE_MISMATCH; -+ } -+ if (ba->status == EXPIRED_HOME_NONCE_INDEX || ba->status == EXPIRED_NONCES) { -+ if (binding->rr) { -+ /* Need to resend home test init to CN */ -+ binding->rr->rr_state |= RR_WAITH; -+ mipv6_send_addr_test_init(&binding->home_addr, -+ &binding->cn_addr, -+ MIPV6_MH_HOTI, -+ binding->rr->hot_cookie); -+ MIPV6_INC_STATS(n_ban_rcvd); -+ } else { -+ DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_NONCE_INDEX" -+ "for non-RR BU"); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ } -+ write_unlock(&bul_lock); -+ return 0; -+ } -+ if (ba->status == EXPIRED_CAREOF_NONCE_INDEX || ba->status == EXPIRED_NONCES) { -+ if (binding->rr) { -+ /* Need to resend care-of test init to CN */ -+ binding->rr->rr_state |= RR_WAITC; -+ mipv6_send_addr_test_init(&binding->coa, -+ &binding->cn_addr, -+ MIPV6_MH_COTI, -+ binding->rr->cot_cookie); -+ MIPV6_INC_STATS(n_ban_rcvd); -+ } else { -+ DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_CAREOF_INDEX" -+ "for non-RR BU"); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ } -+ write_unlock(&bul_lock); -+ return 0; -+ } -+ write_unlock(&bul_lock); -+ -+ if (ba->status >= REASON_UNSPECIFIED) { -+ DEBUG(DBG_INFO, "Binding Ack status : %d indicates error", ba->status); -+ mipv6_ba_rcvd(ifindex, src, home, sequence, lifetime, -+ refresh, ba->status); -+ MIPV6_INC_STATS(n_ban_rcvd); -+ return 0; -+ } -+ MIPV6_INC_STATS(n_ba_rcvd); -+ if (mipv6_ba_rcvd(ifindex, src, home, ntohs(ba->sequence), lifetime, -+ refresh, ba->status)) { -+ DEBUG(DBG_WARNING, "mipv6_ba_rcvd failed"); -+ } -+ -+ return 0; -+} -+ -+/** -+ * mipv6_handle_mh_be - Binding Error handler -+ * @cn: source of this packet -+ * @coa: care-of address -+ * @home: home address -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ **/ -+ -+static int mipv6_handle_mh_be(struct sk_buff *skb, -+ struct in6_addr *home, -+ struct in6_addr *coa, -+ struct in6_addr *cn, -+ struct in6_addr *unused, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_be *be = (struct mipv6_mh_be *)mh->data; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ struct in6_addr *hoa; -+ struct bul_inval_args args; -+ -+ DEBUG_FUNC(); -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*be); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_WARNING, "Mobility Header length less than BE"); -+ MIPV6_INC_STATS(n_be_drop.invalid); -+ return -1; -+ } -+ -+ -+ if (!ipv6_addr_any(&be->home_addr)) -+ hoa = &be->home_addr; -+ else -+ hoa = home; -+ -+ MIPV6_INC_STATS(n_be_rcvd); -+ -+ args.all_rr_states = 0; -+ args.cn = cn; -+ args.mn = hoa; -+ -+ switch (be->status) { -+ case 1: /* Home Address Option used without a binding */ -+ /* Get ULP information about CN-MN communication. If -+ nothing in progress, MUST delete. Otherwise MAY -+ ignore. */ -+ args.all_rr_states = 1; -+ case 2: /* Received unknown MH type */ -+ /* If not expecting ack, SHOULD ignore. If MH -+ extension in use, stop it. If not, stop RO for -+ this CN. */ -+ write_lock(&bul_lock); -+ mipv6_bul_iterate(mn_bul_invalidate, &args); -+ write_unlock(&bul_lock); -+ break; -+ } -+ -+ return 0; -+} -+ -+/* -+ * mipv6_bu_rate_limit() : Takes a bulentry, a COA and 'flags' to check -+ * whether BU being sent is for Home Registration or not. -+ * -+ * If the number of BU's sent is fewer than MAX_FAST_UPDATES, this BU -+ * is allowed to be sent at the MAX_UPDATE_RATE. -+ * If the number of BU's sent is greater than or equal to MAX_FAST_UPDATES, -+ * this BU is allowed to be sent at the SLOW_UPDATE_RATE. -+ * -+ * Assumption : This function is not re-entrant. and the caller holds the -+ * bulentry lock (by calling mipv6_bul_get()) to stop races with other -+ * CPU's executing this same function. -+ * -+ * Side-Effects. Either of the following could on success : -+ * 1. Sets consecutive_sends to 1 if the entry is a Home agent -+ * registration or the COA has changed. -+ * 2. Increments consecutive_sends if the number of BU's sent so -+ * far is less than MAX_FAST_UPDATES, and this BU is being sent -+ * atleast MAX_UPDATE_RATE after previous one. -+ * -+ * Return Value : 0 on Success, -1 on Failure -+ */ -+static int mipv6_bu_rate_limit(struct mipv6_bul_entry *bulentry, -+ struct in6_addr *coa, __u8 flags) -+{ -+ if ((flags & MIPV6_BU_F_HOME) || ipv6_addr_cmp(&bulentry->coa, coa)) { -+ /* Home Agent Registration or different COA - restart from 1 */ -+ bulentry->consecutive_sends = 1; -+ return 0; -+ } -+ -+ if (bulentry->consecutive_sends < MAX_FAST_UPDATES) { -+ /* First MAX_FAST_UPDATES can be sent at MAX_UPDATE_RATE */ -+ if (jiffies - bulentry->lastsend < MAX_UPDATE_RATE * HZ) { -+ return -1; -+ } -+ bulentry->consecutive_sends ++; -+ } else { -+ /* Remaining updates SHOULD be sent at SLOW_UPDATE_RATE */ -+ if (jiffies - bulentry->lastsend < SLOW_UPDATE_RATE * HZ) { -+ return -1; -+ } -+ /* Don't inc 'consecutive_sends' to avoid overflow to zero */ -+ } -+ /* OK to send a BU */ -+ return 0; -+} -+ -+/** -+ * mipv6_send_bu - send a Binding Update -+ * @saddr: source address for BU -+ * @daddr: destination address for BU -+ * @coa: care-of address for MN -+ * @initdelay: initial BA wait timeout -+ * @maxackdelay: maximum BA wait timeout -+ * @exp: exponention back off -+ * @flags: flags for BU -+ * @lifetime: granted lifetime for binding -+ * @ops: mobility options -+ * -+ * Send a binding update. 'flags' may contain any of %MIPV6_BU_F_ACK, -+ * %MIPV6_BU_F_HOME, %MIPV6_BU_F_ROUTER bitwise ORed. If -+ * %MIPV6_BU_F_ACK is included retransmission will be attempted until -+ * the update has been acknowledged. Retransmission is done if no -+ * acknowledgement is received within @initdelay seconds. @exp -+ * specifies whether to use exponential backoff (@exp != 0) or linear -+ * backoff (@exp == 0). For exponential backoff the time to wait for -+ * an acknowledgement is doubled on each retransmission until a delay -+ * of @maxackdelay, after which retransmission is no longer attempted. -+ * For linear backoff the delay is kept constant and @maxackdelay -+ * specifies the maximum number of retransmissions instead. If -+ * sub-options are present ops must contain all sub-options to be -+ * added. On a mobile node, use the mobile node's home address for -+ * @saddr. Returns 0 on success, non-zero on failure. -+ * -+ * Caller may not hold @bul_lock. -+ **/ -+int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *coa, u32 initdelay, -+ u32 maxackdelay, u8 exp, u8 flags, u32 lifetime, -+ struct mipv6_mh_opt *ops) -+{ -+ int ret; -+ __u8 state; -+ __u16 seq = 0; -+ int (*callback)(struct mipv6_bul_entry *); -+ __u32 callback_time; -+ struct mipv6_bul_entry *bulentry; -+ -+ /* First a sanity check: don't send BU to local addresses */ -+ if(ipv6_chk_addr(daddr, NULL)) { -+ DEBUG(DBG_ERROR, "BUG: Trying to send BU to local address"); -+ return -1; -+ } -+ DEBUG(DBG_INFO, "Sending BU to CN %x:%x:%x:%x:%x:%x:%x:%x " -+ "for home address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(daddr), NIPV6ADDR(saddr)); -+ -+ if ((bulentry = mipv6_bul_get(daddr, saddr)) != NULL) { -+ if (bulentry->state == ACK_ERROR) { -+ /* -+ * Don't send any more BU's to nodes which don't -+ * understanding one. -+ */ -+ DEBUG(DBG_INFO, "Not sending BU to node which doesn't" -+ " understand one"); -+ return -1; -+ } -+ if (mipv6_bu_rate_limit(bulentry, coa, flags) < 0) { -+ DEBUG(DBG_DATADUMP, "Limiting BU sent."); -+ return 0; -+ } -+ } -+ -+ switch (mipv6_rr_state(bulentry, saddr, coa, flags)) { -+ case INPROGRESS_RR: -+ /* We are already doing RR, don't do BU at this time, it is -+ * done automatically later */ -+ DEBUG(DBG_INFO, "RR in progress not sending BU"); -+ return 0; -+ -+ case DO_RR: -+ /* Just do RR and return, BU is done automatically later */ -+ DEBUG(DBG_INFO, "starting RR" ); -+ mipv6_RR_start(saddr, daddr, coa, bulentry, initdelay, -+ maxackdelay, flags, lifetime, ops); -+ return 0; -+ -+ case NO_RR: -+ DEBUG(DBG_DATADUMP, "No RR necessary" ); -+ default: -+ break; -+ } -+ -+ if (bulentry) -+ seq = bulentry->seq + 1; -+ -+ /* Add to binding update list */ -+ -+ if (flags & MIPV6_BU_F_ACK) { -+ DEBUG(DBG_INFO, "Setting bul callback to bul_resend_exp"); -+ /* Send using exponential backoff */ -+ state = RESEND_EXP; -+ callback = bul_resend_exp; -+ callback_time = initdelay; -+ } else { -+ DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired"); -+ /* No acknowledgement/resending required */ -+ state = ACK_OK; /* pretend we got an ack */ -+ callback = bul_entry_expired; -+ callback_time = lifetime; -+ } -+ -+ /* BU only for the home address */ -+ /* We must hold bul_lock (write) while calling add */ -+ if ((bulentry = mipv6_bul_add(daddr, saddr, coa, lifetime, seq, -+ flags, callback, callback_time, -+ state, initdelay, maxackdelay, ops, -+ NULL)) == NULL) { -+ DEBUG(DBG_INFO, "couldn't update BUL"); -+ return 0; -+ } -+ ret = send_bu_msg(bulentry); -+ -+ return ret; -+} -+ -+int __init mipv6_mh_mn_init(void) -+{ -+ mipv6_mh_register(MIPV6_MH_HOT, mipv6_handle_mh_HC_test); -+ mipv6_mh_register(MIPV6_MH_COT, mipv6_handle_mh_HC_test); -+ mipv6_mh_register(MIPV6_MH_BA, mipv6_handle_mh_ba); -+ mipv6_mh_register(MIPV6_MH_BRR, mipv6_handle_mh_brr); -+ mipv6_mh_register(MIPV6_MH_BE, mipv6_handle_mh_be); -+ -+ return 0; -+} -+ -+void __exit mipv6_mh_mn_exit(void) -+{ -+ mipv6_mh_unregister(MIPV6_MH_HOT); -+ mipv6_mh_unregister(MIPV6_MH_COT); -+ mipv6_mh_unregister(MIPV6_MH_BA); -+ mipv6_mh_unregister(MIPV6_MH_BRR); -+ mipv6_mh_unregister(MIPV6_MH_BE); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_cn.c linux-2.4.25/net/ipv6/mobile_ip6/module_cn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/module_cn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/module_cn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,167 @@ -+/* -+ * Mobile IPv6 Common Module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/init.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/mipglue.h> -+ -+#include "bcache.h" -+#include "mipv6_icmp.h" -+#include "stats.h" -+#include "mobhdr.h" -+#include "exthdrs.h" -+ -+int mipv6_debug = 1; -+ -+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 -+MODULE_AUTHOR("MIPL Team"); -+MODULE_DESCRIPTION("Mobile IPv6"); -+MODULE_LICENSE("GPL"); -+MODULE_PARM(mipv6_debug, "i"); -+#endif -+ -+#include "config.h" -+ -+struct mip6_func mip6_fn; -+struct mip6_conf mip6node_cnf = { -+ capabilities: CAP_CN, -+ accept_ret_rout: 1, -+ max_rtr_reachable_time: 0, -+ eager_cell_switching: 0, -+ max_num_tunnels: 0, -+ min_num_tunnels: 0, -+ binding_refresh_advice: 0, -+ bu_lladdr: 0, -+ bu_keymgm: 0, -+ bu_cn_ack: 0 -+}; -+ -+#define MIPV6_BCACHE_SIZE 128 -+ -+/********************************************************************** -+ * -+ * MIPv6 CN Module Init / Cleanup -+ * -+ **********************************************************************/ -+ -+#ifdef CONFIG_SYSCTL -+/* Sysctl table */ -+ctl_table mipv6_mobility_table[] = { -+ {NET_IPV6_MOBILITY_DEBUG, "debuglevel", -+ &mipv6_debug, sizeof(int), 0644, NULL, -+ &proc_dointvec}, -+ {NET_IPV6_MOBILITY_RETROUT, "accept_return_routability", -+ &mip6node_cnf.accept_ret_rout, sizeof(int), 0644, NULL, -+ &proc_dointvec}, -+ {0} -+}; -+ctl_table mipv6_table[] = { -+ {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, -+ {0} -+}; -+ -+static struct ctl_table_header *mipv6_sysctl_header; -+static struct ctl_table mipv6_net_table[]; -+static struct ctl_table mipv6_root_table[]; -+ -+ctl_table mipv6_net_table[] = { -+ {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, -+ {0} -+}; -+ -+ctl_table mipv6_root_table[] = { -+ {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, -+ {0} -+}; -+#endif /* CONFIG_SYSCTL */ -+ -+extern void mipv6_rr_init(void); -+ -+/* Initialize the module */ -+static int __init mip6_init(void) -+{ -+ int err = 0; -+ -+ printk(KERN_INFO "MIPL Mobile IPv6 for Linux Correspondent Node %s (%s)\n", -+ MIPLVERSION, MIPV6VERSION); -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+ printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); -+#endif -+ -+ if ((err = mipv6_bcache_init(MIPV6_BCACHE_SIZE)) < 0) -+ goto bcache_fail; -+ -+ if ((err = mipv6_icmpv6_init()) < 0) -+ goto icmp_fail; -+ -+ if ((err = mipv6_stats_init()) < 0) -+ goto stats_fail; -+ mipv6_rr_init(); -+ -+#ifdef CONFIG_SYSCTL -+ mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); -+#endif -+ -+ if ((err = mipv6_mh_common_init()) < 0) -+ goto mh_fail; -+ -+ MIPV6_SETCALL(mipv6_modify_txoptions, mipv6_modify_txoptions); -+ -+ MIPV6_SETCALL(mipv6_handle_homeaddr, mipv6_handle_homeaddr); -+ MIPV6_SETCALL(mipv6_icmp_swap_addrs, mipv6_icmp_swap_addrs); -+ -+ return 0; -+ -+mh_fail: -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ mipv6_stats_exit(); -+stats_fail: -+ mipv6_icmpv6_exit(); -+icmp_fail: -+ mipv6_bcache_exit(); -+bcache_fail: -+ return err; -+} -+module_init(mip6_init); -+ -+#ifdef MODULE -+/* Cleanup module */ -+static void __exit mip6_exit(void) -+{ -+ printk(KERN_INFO "mip6_base.o exiting.\n"); -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ -+ /* Invalidate all custom kernel hooks. No need to do this -+ separately for all hooks. */ -+ mipv6_invalidate_calls(); -+ -+ mipv6_mh_common_exit(); -+ mipv6_stats_exit(); -+ mipv6_icmpv6_exit(); -+ mipv6_bcache_exit(); -+} -+module_exit(mip6_exit); -+#endif /* MODULE */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_ha.c linux-2.4.25/net/ipv6/mobile_ip6/module_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/module_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/module_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,264 @@ -+/* -+ * Mobile IPv6 Home Agent Module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/init.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/mipglue.h> -+#include <net/addrconf.h> -+ -+#include "mobhdr.h" -+#include "tunnel_ha.h" -+#include "ha.h" -+#include "halist.h" -+#include "mipv6_icmp.h" -+//#include "prefix.h" -+#include "bcache.h" -+#include "debug.h" -+ -+int mipv6_use_auth = 0; -+ -+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 -+MODULE_AUTHOR("MIPL Team"); -+MODULE_DESCRIPTION("Mobile IPv6 Home Agent"); -+MODULE_LICENSE("GPL"); -+#endif -+ -+#include "config.h" -+ -+#define MIPV6_HALIST_SIZE 128 -+struct ha_info_opt { -+ u8 type; -+ u8 len; -+ u16 res; -+ u16 pref; -+ u16 ltime; -+}; -+/* -+ * Called from ndisc.c's router_discovery. -+ */ -+static int mipv6_ha_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts) -+{ -+ unsigned int ha_info_pref = 0, ha_info_lifetime; -+ int ifi = ((struct inet6_skb_parm *)skb->cb)->iif; -+ struct ra_msg *ra = (struct ra_msg *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr ll_addr; -+ struct hal { -+ struct in6_addr prefix; -+ int plen; -+ struct hal *next; -+ }; -+ -+ DEBUG_FUNC(); -+ -+ ha_info_lifetime = ntohs(ra->icmph.icmp6_rt_lifetime); -+ ipv6_addr_copy(&ll_addr, saddr); -+ -+ if (ndopts->nd_opts_hai) { -+ struct ha_info_opt *hai = (struct ha_info_opt *)ndopts->nd_opts_hai; -+ ha_info_pref = ntohs(hai->pref); -+ ha_info_lifetime = ntohs(hai->ltime); -+ DEBUG(DBG_DATADUMP, -+ "received home agent info with preference : %d and lifetime : %d", -+ ha_info_pref, ha_info_lifetime); -+ } -+ if (ndopts->nd_opts_pi) { -+ struct nd_opt_hdr *p; -+ for (p = ndopts->nd_opts_pi; -+ p; -+ p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) { -+ struct prefix_info *pinfo; -+ -+ pinfo = (struct prefix_info *) p; -+ -+ if (pinfo->router_address) { -+ DEBUG(DBG_DATADUMP, "Adding router address to " -+ "ha queue \n"); -+ /* If RA has H bit set and Prefix Info -+ * Option R bit set, queue this -+ * address to be added to Home Agents -+ * List. -+ */ -+ if (ipv6_addr_type(&pinfo->prefix) & -+ IPV6_ADDR_LINKLOCAL) -+ continue; -+ if (!ra->icmph.icmp6_home_agent || !ha_info_lifetime) { -+ mipv6_halist_delete(&pinfo->prefix); -+ continue; -+ } else { -+ -+ mipv6_halist_add(ifi, &pinfo->prefix, -+ pinfo->prefix_len, &ll_addr, -+ ha_info_pref, ha_info_lifetime); -+ } -+ -+ } -+ -+ } -+ } -+ return MIPV6_ADD_RTR; -+} -+ -+/********************************************************************** -+ * -+ * MIPv6 Module Init / Cleanup -+ * -+ **********************************************************************/ -+ -+#ifdef CONFIG_SYSCTL -+/* Sysctl table */ -+extern int -+mipv6_max_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+extern int -+mipv6_min_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+int max_adv = ~(u16)0; -+int min_zero = 0; -+ctl_table mipv6_mobility_table[] = { -+ {NET_IPV6_MOBILITY_BINDING_REFRESH, "binding_refresh_advice", -+ &mip6node_cnf.binding_refresh_advice, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_adv}, -+ -+ {NET_IPV6_MOBILITY_MAX_TNLS, "max_tnls", &mipv6_max_tnls, sizeof(int), -+ 0644, NULL, &mipv6_max_tnls_sysctl}, -+ {NET_IPV6_MOBILITY_MIN_TNLS, "min_tnls", &mipv6_min_tnls, sizeof(int), -+ 0644, NULL, &mipv6_min_tnls_sysctl}, -+ {0} -+}; -+ctl_table mipv6_table[] = { -+ {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, -+ {0} -+}; -+ -+static struct ctl_table_header *mipv6_sysctl_header; -+static struct ctl_table mipv6_net_table[]; -+static struct ctl_table mipv6_root_table[]; -+ -+ctl_table mipv6_net_table[] = { -+ {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, -+ {0} -+}; -+ -+ctl_table mipv6_root_table[] = { -+ {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, -+ {0} -+}; -+#endif /* CONFIG_SYSCTL */ -+ -+extern void mipv6_check_dad(struct in6_addr *haddr); -+extern void mipv6_dad_init(void); -+extern void mipv6_dad_exit(void); -+extern int mipv6_forward(struct sk_buff *); -+ -+/* Initialize the module */ -+static int __init mip6_ha_init(void) -+{ -+ int err = 0; -+ -+ printk(KERN_INFO "MIPL Mobile IPv6 for Linux Home Agent %s (%s)\n", -+ MIPLVERSION, MIPV6VERSION); -+ mip6node_cnf.capabilities = CAP_CN | CAP_HA; -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_rcv_dhaad_req; -+ mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_no_rcv; -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+ printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); -+#endif -+ -+#ifdef CONFIG_SYSCTL -+ mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); -+#endif -+ mipv6_initialize_tunnel(); -+ -+ if ((err = mipv6_ha_init()) < 0) -+ goto ha_fail; -+ -+ MIPV6_SETCALL(mipv6_ra_rcv, mipv6_ha_ra_rcv); -+ MIPV6_SETCALL(mipv6_forward, mipv6_forward); -+ mipv6_dad_init(); -+ MIPV6_SETCALL(mipv6_check_dad, mipv6_check_dad); -+ -+ if ((err = mipv6_halist_init(MIPV6_HALIST_SIZE)) < 0) -+ goto halist_fail; -+ -+// mipv6_initialize_pfx_icmpv6(); -+ -+ return 0; -+ -+halist_fail: -+ mipv6_dad_exit(); -+ mipv6_ha_exit(); -+ha_fail: -+ mipv6_shutdown_tunnel(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ MIPV6_RESETCALL(mipv6_ra_rcv); -+ MIPV6_RESETCALL(mipv6_forward); -+ MIPV6_RESETCALL(mipv6_check_dad); -+ -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ return err; -+} -+module_init(mip6_ha_init); -+ -+#ifdef MODULE -+/* Cleanup module */ -+static void __exit mip6_ha_exit(void) -+{ -+ printk(KERN_INFO "mip6_ha.o exiting.\n"); -+ mip6node_cnf.capabilities &= ~(int)CAP_HA; -+ -+ mipv6_bcache_cleanup(HOME_REGISTRATION); -+ -+ MIPV6_RESETCALL(mipv6_ra_rcv); -+ MIPV6_RESETCALL(mipv6_forward); -+ MIPV6_RESETCALL(mipv6_check_dad); -+ -+ mipv6_halist_exit(); -+// mipv6_shutdown_pfx_icmpv6(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ mipv6_dad_exit(); -+ mipv6_ha_exit(); -+ mipv6_shutdown_tunnel(); -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+} -+module_exit(mip6_ha_exit); -+#endif /* MODULE */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/module_mn.c linux-2.4.25/net/ipv6/mobile_ip6/module_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/module_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/module_mn.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,188 @@ -+/* -+ * Mobile IPv6 Mobile Node Module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/init.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/mipglue.h> -+ -+extern int mipv6_debug; -+int mipv6_use_auth = 0; -+ -+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 -+MODULE_AUTHOR("MIPL Team"); -+MODULE_DESCRIPTION("Mobile IPv6 Mobile Node"); -+MODULE_LICENSE("GPL"); -+MODULE_PARM(mipv6_debug, "i"); -+#endif -+ -+#include "config.h" -+ -+#include "mobhdr.h" -+#include "mn.h" -+#include "mipv6_icmp.h" -+//#include "prefix.h" -+ -+/* TODO: These will go as soon as we get rid of the last two ioctls */ -+extern int mipv6_ioctl_mn_init(void); -+extern void mipv6_ioctl_mn_exit(void); -+ -+/********************************************************************** -+ * -+ * MIPv6 Module Init / Cleanup -+ * -+ **********************************************************************/ -+ -+#ifdef CONFIG_SYSCTL -+/* Sysctl table */ -+ -+extern int max_rtr_reach_time; -+extern int eager_cell_switching; -+ -+static int max_reach = 1000; -+static int min_reach = 1; -+static int max_one = 1; -+static int min_zero = 0; -+ -+extern int -+mipv6_mdetect_mech_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+extern int -+mipv6_router_reach_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+ctl_table mipv6_mobility_table[] = { -+ {NET_IPV6_MOBILITY_BU_F_LLADDR, "bu_flag_lladdr", -+ &mip6node_cnf.bu_lladdr, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ {NET_IPV6_MOBILITY_BU_F_KEYMGM, "bu_flag_keymgm", -+ &mip6node_cnf.bu_keymgm, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ {NET_IPV6_MOBILITY_BU_F_CN_ACK, "bu_flag_cn_ack", -+ &mip6node_cnf.bu_cn_ack, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ -+ {NET_IPV6_MOBILITY_ROUTER_REACH, "max_router_reachable_time", -+ &max_rtr_reach_time, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_reach, &max_reach}, -+ -+ {NET_IPV6_MOBILITY_MDETECT_MECHANISM, "eager_cell_switching", -+ &eager_cell_switching, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ -+ {0} -+}; -+ctl_table mipv6_table[] = { -+ {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, -+ {0} -+}; -+ -+static struct ctl_table_header *mipv6_sysctl_header; -+static struct ctl_table mipv6_net_table[]; -+static struct ctl_table mipv6_root_table[]; -+ -+ctl_table mipv6_net_table[] = { -+ {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, -+ {0} -+}; -+ -+ctl_table mipv6_root_table[] = { -+ {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, -+ {0} -+}; -+#endif /* CONFIG_SYSCTL */ -+ -+/* Initialize the module */ -+static int __init mip6_mn_init(void) -+{ -+ int err = 0; -+ -+ printk(KERN_INFO "MIPL Mobile IPv6 for Linux Mobile Node %s (%s)\n", -+ MIPLVERSION, MIPV6VERSION); -+ mip6node_cnf.capabilities = CAP_CN | CAP_MN; -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+ printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); -+#endif -+ -+#ifdef CONFIG_SYSCTL -+ mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); -+#endif -+ if ((err = mipv6_mn_init()) < 0) -+ goto mn_fail; -+ -+ mipv6_mh_mn_init(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_rcv_dhaad_rep; -+ mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_rcv_paramprob; -+ -+// mipv6_initialize_pfx_icmpv6(); -+ -+ if ((err = mipv6_ioctl_mn_init()) < 0) -+ goto ioctl_fail; -+ -+ return 0; -+ -+ioctl_fail: -+// mipv6_shutdown_pfx_icmpv6(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ mipv6_mh_mn_exit(); -+ mipv6_mn_exit(); -+mn_fail: -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ return err; -+} -+module_init(mip6_mn_init); -+ -+#ifdef MODULE -+/* Cleanup module */ -+static void __exit mip6_mn_exit(void) -+{ -+ printk(KERN_INFO "mip6_mn.o exiting.\n"); -+ mip6node_cnf.capabilities &= ~(int)CAP_MN; -+ -+ mipv6_ioctl_mn_exit(); -+// mipv6_shutdown_pfx_icmpv6(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ mipv6_mn_exit(); -+ -+/* common cleanup */ -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+} -+module_exit(mip6_mn_exit); -+#endif /* MODULE */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.c linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,287 @@ -+/* -+ * 2001 (c) Oy L M Ericsson Ab -+ * -+ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com> -+ * -+ * $Id$ -+ * -+ */ -+ -+/* -+ * Vertical hand-off information manager -+ */ -+ -+#include <linux/netdevice.h> -+#include <linux/in6.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/proc_fs.h> -+#include <linux/string.h> -+#include <linux/kernel.h> -+#include <asm/io.h> -+#include <asm/uaccess.h> -+#include <linux/list.h> -+#include "multiaccess_ctl.h" -+#include "debug.h" -+ -+/* -+ * Local variables -+ */ -+static LIST_HEAD(if_list); -+ -+/* Internal interface information list */ -+struct ma_if_info { -+ struct list_head list; -+ int interface_id; -+ int preference; -+ __u8 status; -+}; -+ -+/** -+ * ma_ctl_get_preference - get preference value for interface -+ * @ifi: interface index -+ * -+ * Returns integer value preference for given interface. -+ **/ -+int ma_ctl_get_preference(int ifi) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ int pref = 0; -+ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == ifi) { -+ pref = info->preference; -+ return pref; -+ } -+ } -+ return -1; -+} -+/** -+ * ma_ctl_get_preference - get preference value for interface -+ * @ifi: interface index -+ * -+ * Returns integer value interface index for interface with highest preference. -+ **/ -+int ma_ctl_get_preferred_if(void) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info, *pref_if = NULL; -+ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (!pref_if || (info->preference > pref_if->preference)) { -+ pref_if = info; -+ } -+ } -+ if (pref_if) return pref_if->interface_id; -+ return 0; -+} -+/** -+ * ma_ctl_set_preference - set preference for interface -+ * @arg: ioctl args -+ * -+ * Sets preference of an existing interface (called by ioctl). -+ **/ -+void ma_ctl_set_preference(unsigned long arg) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ struct ma_if_uinfo uinfo; -+ -+ memset(&uinfo, 0, sizeof(struct ma_if_uinfo)); -+ if (copy_from_user(&uinfo, (struct ma_if_uinfo *)arg, -+ sizeof(struct ma_if_uinfo)) < 0) { -+ DEBUG(DBG_WARNING, "copy_from_user failed"); -+ return; -+ } -+ -+ /* check if the interface exists */ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == uinfo.interface_id) { -+ info->preference = uinfo.preference; -+ return; -+ } -+ } -+} -+ -+/** -+ * ma_ctl_add_iface - add new interface to list -+ * @if_index: interface index -+ * -+ * Adds new interface entry to preference list. Preference is set to -+ * the same value as @if_index. Entry @status is set to -+ * %MA_IFACE_NOT_USED. -+ **/ -+void ma_ctl_add_iface(int if_index) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ -+ DEBUG_FUNC(); -+ -+ /* check if the interface already exists */ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == if_index) { -+ info->status = MA_IFACE_NOT_USED; -+ info->preference = if_index; -+ return; -+ } -+ } -+ -+ info = kmalloc(sizeof(struct ma_if_info), GFP_ATOMIC); -+ if (info == NULL) { -+ DEBUG(DBG_ERROR, "Out of memory"); -+ return; -+ } -+ memset(info, 0, sizeof(struct ma_if_info)); -+ info->interface_id = if_index; -+ info->preference = if_index; -+ info->status = MA_IFACE_NOT_USED; -+ list_add(&info->list, &if_list); -+} -+ -+/** -+ * ma_ctl_del_iface - remove entry from the list -+ * @if_index: interface index -+ * -+ * Removes entry for interface @if_index from preference list. -+ **/ -+int ma_ctl_del_iface(int if_index) -+{ -+ struct list_head *lh, *next; -+ struct ma_if_info *info; -+ -+ DEBUG_FUNC(); -+ -+ /* if the iface exists, change availability to 0 */ -+ list_for_each_safe(lh, next, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == if_index) { -+ list_del(&info->list); -+ kfree(info); -+ return 0; -+ } -+ } -+ -+ return -1; -+} -+ -+/** -+ * ma_ctl_upd_iface - update entry (and list) -+ * @if_index: interface to update -+ * @status: new status for interface -+ * @change_if_index: new interface -+ * -+ * Updates @if_index entry on preference list. Entry status is set to -+ * @status. If new @status is %MA_IFACE_CURRENT, updates list to have -+ * only one current device. If @status is %MA_IFACE_NOT_PRESENT, -+ * entry is deleted and further if entry had %MA_IFACE_CURRENT set, -+ * new current device is looked up and returned in @change_if_index. -+ * New preferred interface is also returned if current device changes -+ * to %MA_IFACE_NOT_USED. Returns 0 on success, otherwise negative. -+ **/ -+int ma_ctl_upd_iface(int if_index, int status, int *change_if_index) -+{ -+ struct list_head *lh, *tmp; -+ struct ma_if_info *info, *pref = NULL; -+ int found = 0; -+ -+ DEBUG_FUNC(); -+ -+ *change_if_index = 0; -+ -+ /* check if the interface exists */ -+ list_for_each_safe(lh, tmp, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (status == MA_IFACE_NOT_PRESENT) { -+ if (info->interface_id == if_index) { -+ list_del_init(&info->list); -+ kfree(info); -+ found = 1; -+ break; -+ } -+ } else if (status == MA_IFACE_CURRENT) { -+ if (info->interface_id == if_index) { -+ info->status |= MA_IFACE_CURRENT; -+ found = 1; -+ } else { -+ info->status |= MA_IFACE_NOT_USED; -+ } -+ } else if (status == MA_IFACE_NOT_USED) { -+ if (info->interface_id == if_index) { -+ if (info->status | MA_IFACE_CURRENT) { -+ found = 1; -+ } -+ info->status &= !MA_IFACE_CURRENT; -+ info->status |= MA_IFACE_NOT_USED; -+ info->status &= !MA_IFACE_HAS_ROUTER; -+ } -+ break; -+ } else if (status == MA_IFACE_HAS_ROUTER) { -+ if (info->interface_id == if_index) { -+ info->status |= MA_IFACE_HAS_ROUTER; -+ } -+ return 0; -+ } -+ } -+ -+ if (status & (MA_IFACE_NOT_USED|MA_IFACE_NOT_PRESENT) && found) { -+ /* select new interface */ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (pref == NULL || ((info->preference > pref->preference) && -+ info->status & MA_IFACE_HAS_ROUTER)) -+ pref = info; -+ } -+ if (pref) { -+ *change_if_index = pref->interface_id; -+ pref->status |= MA_IFACE_CURRENT; -+ } else { -+ *change_if_index = -1; -+ } -+ return 0; -+ } -+ -+ if (found) return 0; -+ -+ return -1; -+} -+ -+static int if_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ int len = 0; -+ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ len += sprintf(buffer + len, "%02d %010d %1d %1d\n", -+ info->interface_id, info->preference, -+ !!(info->status & MA_IFACE_HAS_ROUTER), -+ !!(info->status & MA_IFACE_CURRENT)); -+ } -+ -+ *start = buffer + offset; -+ -+ len -= offset; -+ -+ if (len > length) len = length; -+ -+ return len; -+ -+} -+ -+void ma_ctl_init(void) -+{ -+ proc_net_create("mip6_iface", 0, if_proc_info); -+} -+ -+void ma_ctl_clean(void) -+{ -+ proc_net_remove("mip6_iface"); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.h linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/multiaccess_ctl.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/multiaccess_ctl.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,77 @@ -+/* -+ * 2001 (c) Oy L M Ericsson Ab -+ * -+ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com> -+ * -+ * $Id$ -+ * -+ */ -+ -+#ifndef _MULTIACCESS_CTL_H -+#define _MULTIACCESS_CTL_H -+ -+/* status */ -+#define MA_IFACE_NOT_PRESENT 0x01 -+#define MA_IFACE_NOT_USED 0x02 -+#define MA_IFACE_HAS_ROUTER 0x04 -+#define MA_IFACE_CURRENT 0x10 -+ -+struct ma_if_uinfo { -+ int interface_id; -+ int preference; -+ __u8 status; -+}; -+/* -+ * @ma_ctl_get_preferred_id: returns most preferred interface id -+ */ -+int ma_ctl_get_preferred_if(void); -+ -+/* @ma_ctl_get_preference: returns preference for an interface -+ * @name: name of the interface (dev->name) -+ */ -+int ma_ctl_get_preference(int ifi); -+ -+/* -+ * Public function: ma_ctl_set_preference -+ * Description: Set preference of an existing interface (called by ioctl) -+ * Returns: -+ */ -+void ma_ctl_set_preference(unsigned long); -+ -+/* -+ * Public function: ma_ctl_add_iface -+ * Description: Inform control module to insert a new interface -+ * Returns: 0 if success, any other number means an error -+ */ -+void ma_ctl_add_iface(int); -+ -+/* -+ * Public function: ma_ctl_del_iface -+ * Description: Inform control module to remove an obsolete interface -+ * Returns: 0 if success, any other number means an error -+ */ -+int ma_ctl_del_iface(int); -+ -+/* -+ * Public function: ma_ctl_upd_iface -+ * Description: Inform control module of status change. -+ * Returns: 0 if success, any other number means an error -+ */ -+int ma_ctl_upd_iface(int, int, int *); -+ -+/* -+ * Public function: ma_ctl_init -+ * Description: XXX -+ * Returns: XXX -+ */ -+void ma_ctl_init(void); -+ -+/* -+ * Public function: ma_ctl_clean -+ * Description: XXX -+ * Returns: - -+ */ -+void ma_ctl_clean(void); -+ -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/ndisc_ha.c linux-2.4.25/net/ipv6/mobile_ip6/ndisc_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/ndisc_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/ndisc_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,596 @@ -+/* -+ * Mobile IPv6 Duplicate Address Detection Functions -+ * -+ * Authors: -+ * Krishna Kumar <krkumar@us.ibm.com> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/in6.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "debug.h" -+#include "bcache.h" -+#include "ha.h" /* mipv6_generate_ll_addr */ -+ -+/* -+ * Binding Updates from MN are cached in this structure till DAD is performed. -+ * This structure is used to retrieve a pending Binding Update for the HA to -+ * reply to after performing DAD. The first cell is different from the rest as -+ * follows : -+ * 1. The first cell is used to chain the remaining cells. -+ * 2. The timeout of the first cell is used to delete expired entries -+ * in the list of cells, while the timeout of the other cells are -+ * used for timing out a NS request so as to reply to a BU. -+ * 3. The only elements of the first cell that are used are : -+ * next, prev, and callback_timer. -+ * -+ * TODO : Don't we need to do pneigh_lookup on the Link Local address ? -+ */ -+struct mipv6_dad_cell { -+ /* Information needed for DAD management */ -+ struct mipv6_dad_cell *next; /* Next element on the DAD list */ -+ struct mipv6_dad_cell *prev; /* Prev element on the DAD list */ -+ __u16 probes; /* Number of times to probe for addr */ -+ __u16 flags; /* Entry flags - see below */ -+ struct timer_list callback_timer; /* timeout for entry */ -+ -+ /* Information needed for performing DAD */ -+ struct inet6_ifaddr *ifp; -+ int ifindex; -+ struct in6_addr daddr; -+ struct in6_addr haddr; /* home address */ -+ struct in6_addr ll_haddr; /* Link Local value of haddr */ -+ struct in6_addr coa; -+ struct in6_addr rep_coa; -+ __u32 ba_lifetime; -+ __u16 sequence; -+ __u8 bu_flags; -+}; -+ -+/* Values for the 'flags' field in the mipv6_dad_cell */ -+#define DAD_INIT_ENTRY 0 -+#define DAD_DUPLICATE_ADDRESS 1 -+#define DAD_UNIQUE_ADDRESS 2 -+ -+/* Head of the pending DAD list */ -+static struct mipv6_dad_cell dad_cell_head; -+ -+/* Lock to access the pending DAD list */ -+static rwlock_t dad_lock = RW_LOCK_UNLOCKED; -+ -+/* Timer routine which deletes 'expired' entries in the DAD list */ -+static void mipv6_dad_delete_old_entries(unsigned long unused) -+{ -+ struct mipv6_dad_cell *curr, *next; -+ unsigned long next_time = 0; -+ -+ write_lock(&dad_lock); -+ curr = dad_cell_head.next; -+ while (curr != &dad_cell_head) { -+ next = curr->next; -+ if (curr->flags != DAD_INIT_ENTRY) { -+ if (curr->callback_timer.expires <= jiffies) { -+ /* Entry has expired, free it up. */ -+ curr->next->prev = curr->prev; -+ curr->prev->next = curr->next; -+ in6_ifa_put(curr->ifp); -+ kfree(curr); -+ } else if (next_time < -+ curr->callback_timer.expires) { -+ next_time = curr->callback_timer.expires; -+ } -+ } -+ curr = next; -+ } -+ write_unlock(&dad_lock); -+ if (next_time) { -+ /* -+ * Start another timer if more cells need to be removed at -+ * a later stage. -+ */ -+ dad_cell_head.callback_timer.expires = next_time; -+ add_timer(&dad_cell_head.callback_timer); -+ } -+} -+ -+/* -+ * Queue a timeout routine to clean up 'expired' DAD entries. -+ */ -+static void mipv6_start_dad_head_timer(struct mipv6_dad_cell *cell) -+{ -+ unsigned long expire = jiffies + -+ cell->ifp->idev->nd_parms->retrans_time * 10; -+ -+ if (!timer_pending(&dad_cell_head.callback_timer) || -+ expire < dad_cell_head.callback_timer.expires) { -+ /* -+ * Add timer if none pending, or mod the timer if new -+ * cell needs to be expired before existing timer runs. -+ * -+ * We let the cell remain as long as possible, so that -+ * new BU's as part of retransmissions don't have to go -+ * through DAD before replying. -+ */ -+ dad_cell_head.callback_timer.expires = expire; -+ -+ /* -+ * Keep the cell around for atleast some time to handle -+ * retransmissions or BU's due to fast MN movement. This -+ * is needed otherwise a previous timeout can delete all -+ * expired entries including this new one. -+ */ -+ cell->callback_timer.expires = jiffies + -+ cell->ifp->idev->nd_parms->retrans_time * 5; -+ if (!timer_pending(&dad_cell_head.callback_timer)) { -+ add_timer(&dad_cell_head.callback_timer); -+ } else { -+ mod_timer(&dad_cell_head.callback_timer, expire); -+ } -+ } -+} -+ -+ -+/* Join solicited node MC address */ -+static inline void mipv6_join_sol_mc_addr(struct in6_addr *addr, -+ struct net_device *dev) -+{ -+ struct in6_addr maddr; -+ -+ /* Join solicited node MC address */ -+ addrconf_addr_solict_mult(addr, &maddr); -+ ipv6_dev_mc_inc(dev, &maddr); -+} -+ -+/* Leave solicited node MC address */ -+static inline void mipv6_leave_sol_mc_addr(struct in6_addr *addr, -+ struct net_device *dev) -+{ -+ struct in6_addr maddr; -+ -+ addrconf_addr_solict_mult(addr, &maddr); -+ ipv6_dev_mc_dec(dev, &maddr); -+} -+ -+/* Send a NS */ -+static inline void mipv6_dad_send_ns(struct inet6_ifaddr *ifp, -+ struct in6_addr *haddr) -+{ -+ struct in6_addr unspec; -+ struct in6_addr mcaddr; -+ -+ ipv6_addr_set(&unspec, 0, 0, 0, 0); -+ addrconf_addr_solict_mult(haddr, &mcaddr); -+ -+ /* addr is 'unspec' since we treat this address as transient */ -+ ndisc_send_ns(ifp->idev->dev, NULL, haddr, &mcaddr, &unspec); -+} -+ -+/* -+ * Search for a home address in the list of pending DAD's. Called from -+ * Neighbor Advertisement -+ * Return values : -+ * -1 : No DAD entry found for this advertisement, or entry already -+ * finished processing. -+ * 0 : Entry found waiting for DAD to finish. -+ */ -+static int dad_search_haddr(struct in6_addr *ll_haddr, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 * seq, struct inet6_ifaddr **ifp) -+{ -+ struct mipv6_dad_cell *cell; -+ -+ read_lock(&dad_lock); -+ cell = dad_cell_head.next; -+ while (cell != &dad_cell_head && -+ ipv6_addr_cmp(&cell->ll_haddr, ll_haddr) && -+ ipv6_addr_cmp(&cell->haddr, ll_haddr)) { -+ cell = cell->next; -+ } -+ if (cell == &dad_cell_head || cell->flags != DAD_INIT_ENTRY) { -+ /* Not found element, or element already finished processing */ -+ if (cell != &dad_cell_head) { -+ /* -+ * Set the state to DUPLICATE, even if it was UNIQUE -+ * earlier. It is not needed to setup timer via -+ * mipv6_start_dad_head_timer since this must have -+ * already been done. -+ */ -+ cell->flags = DAD_DUPLICATE_ADDRESS; -+ } -+ read_unlock(&dad_lock); -+ return -1; -+ } -+ -+ /* -+ * The NA found an unprocessed entry in the DAD list. Expire this -+ * entry since another node advertised this address. Caller should -+ * reject BU (DAD failed). -+ */ -+ ipv6_addr_copy(daddr, &cell->daddr); -+ ipv6_addr_copy(haddr, &cell->haddr); -+ ipv6_addr_copy(coa, &cell->coa); -+ ipv6_addr_copy(rep_coa, &cell->rep_coa); -+ *seq = cell->sequence; -+ *ifp = cell->ifp; -+ -+ if (del_timer(&cell->callback_timer) == 0) { -+ /* Timer already deleted, race with Timeout Handler */ -+ /* No action needed */ -+ } -+ -+ cell->flags = DAD_DUPLICATE_ADDRESS; -+ -+ /* Now leave this address to avoid future processing of NA's */ -+ mipv6_leave_sol_mc_addr(&cell->ll_haddr, cell->ifp->idev->dev); -+ /* Leave also global address, if link local address was in use */ -+ if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) -+ mipv6_leave_sol_mc_addr(&cell->haddr, cell->ifp->idev->dev); -+ /* Start dad_head timer to remove this entry */ -+ mipv6_start_dad_head_timer(cell); -+ -+ read_unlock(&dad_lock); -+ -+ return 0; -+} -+ -+/* ENTRY routine called via Neighbor Advertisement */ -+void mipv6_check_dad(struct in6_addr *ll_haddr) -+{ -+ struct in6_addr daddr, haddr, coa, rep_coa; -+ struct inet6_ifaddr *ifp; -+ __u16 seq; -+ -+ if (dad_search_haddr(ll_haddr, &daddr, &haddr, &coa, &rep_coa, &seq, -+ &ifp) < 0) { -+ /* -+ * Didn't find entry, or no action needed (the action has -+ * already been performed). -+ */ -+ return; -+ } -+ -+ /* -+ * A DAD cell was present, meaning that there is a pending BU -+ * request for 'haddr' - reject the BU. -+ */ -+ mipv6_bu_finish(ifp, 0, DUPLICATE_ADDR_DETECT_FAIL, -+ &daddr, &haddr, &coa, &rep_coa, 0, seq, 0, NULL); -+ return; -+} -+ -+/* -+ * Check if the passed 'cell' is in the list of pending DAD's. Called from -+ * the Timeout Handler. -+ * -+ * Assumes that the caller is holding the dad_lock in reader mode. -+ */ -+static int dad_search_cell(struct mipv6_dad_cell *cell) -+{ -+ struct mipv6_dad_cell *tmp; -+ -+ tmp = dad_cell_head.next; -+ while (tmp != &dad_cell_head && tmp != cell) { -+ tmp = tmp->next; -+ } -+ if (tmp == cell) { -+ if (cell->flags == DAD_INIT_ENTRY) { -+ /* Found valid entry */ -+ if (--cell->probes == 0) { -+ /* -+ * Retransmission's are over - return success. -+ */ -+ cell->flags = DAD_UNIQUE_ADDRESS; -+ -+ /* -+ * Leave this address to avoid future -+ * processing of NA's. -+ */ -+ mipv6_leave_sol_mc_addr(&cell->ll_haddr, -+ cell->ifp->idev-> -+ dev); -+ if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) -+ mipv6_leave_sol_mc_addr(&cell->haddr, -+ cell->ifp->idev->dev); -+ /* start timeout to delete this cell. */ -+ mipv6_start_dad_head_timer(cell); -+ return 0; -+ } -+ /* -+ * Retransmission not finished, send another NS and -+ * return failure. -+ */ -+ mipv6_dad_send_ns(cell->ifp, &cell->ll_haddr); -+ if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) -+ mipv6_leave_sol_mc_addr(&cell->haddr, -+ cell->ifp->idev->dev); -+ cell->callback_timer.expires = jiffies + -+ cell->ifp->idev->nd_parms->retrans_time; -+ add_timer(&cell->callback_timer); -+ } else { -+ /* -+ * This means that an NA was received before the -+ * timeout and when the state changed from -+ * DAD_INIT_ENTRY, the BU got failed as a result. -+ * There is nothing to be done. -+ */ -+ } -+ } -+ return -1; -+} -+ -+/* ENTRY routine called via Timeout */ -+static void mipv6_dad_timeout(unsigned long arg) -+{ -+ __u8 ba_status = SUCCESS; -+ struct in6_addr daddr; -+ struct in6_addr haddr; -+ struct in6_addr coa; -+ struct in6_addr rep_coa; -+ struct inet6_ifaddr *ifp; -+ int ifindex; -+ __u32 ba_lifetime; -+ __u16 sequence; -+ __u8 flags; -+ struct mipv6_dad_cell *cell = (struct mipv6_dad_cell *) arg; -+ -+ /* -+ * If entry is not in the list, we have already sent BU Failure -+ * after getting a NA. -+ */ -+ read_lock(&dad_lock); -+ if (dad_search_cell(cell) < 0) { -+ /* -+ * 'cell' is no longer valid (may not be in the list or -+ * is already processed, due to NA processing), or NS -+ * retransmissions are not yet over. -+ */ -+ read_unlock(&dad_lock); -+ return; -+ } -+ -+ /* This is the final Timeout. Send Bind Ack Success */ -+ -+ ifp = cell->ifp; -+ ifindex = cell->ifindex; -+ ba_lifetime = cell->ba_lifetime; -+ sequence = cell->sequence; -+ flags = cell->bu_flags; -+ -+ ipv6_addr_copy(&daddr, &cell->daddr); -+ ipv6_addr_copy(&haddr, &cell->haddr); -+ ipv6_addr_copy(&coa, &cell->coa); -+ ipv6_addr_copy(&rep_coa, &cell->rep_coa); -+ read_unlock(&dad_lock); -+ -+ /* Send BU Acknowledgement Success */ -+ mipv6_bu_finish(ifp, ifindex, ba_status, -+ &daddr, &haddr, &coa, &rep_coa, -+ ba_lifetime, sequence, flags, NULL); -+ return; -+} -+ -+/* -+ * Check if original home address exists in our DAD pending list, if so return -+ * the cell. -+ * -+ * Assumes that the caller is holding the dad_lock in writer mode. -+ */ -+static struct mipv6_dad_cell *mipv6_dad_get_cell(struct in6_addr *haddr) -+{ -+ struct mipv6_dad_cell *cell; -+ -+ cell = dad_cell_head.next; -+ while (cell != &dad_cell_head -+ && ipv6_addr_cmp(&cell->haddr, haddr)) { -+ cell = cell->next; -+ } -+ if (cell == &dad_cell_head) { -+ /* Not found element */ -+ return NULL; -+ } -+ return cell; -+} -+ -+/* -+ * Save all parameters needed for doing a Bind Ack in the mipv6_dad_cell -+ * structure. -+ */ -+static void mipv6_dad_save_cell(struct mipv6_dad_cell *cell, -+ struct inet6_ifaddr *ifp, int ifindex, -+ struct in6_addr *daddr, -+ struct in6_addr *haddr, -+ struct in6_addr *coa, -+ struct in6_addr *rep_coa, -+ __u32 ba_lifetime, -+ __u16 sequence, __u8 flags) -+{ -+ in6_ifa_hold(ifp); -+ cell->ifp = ifp; -+ cell->ifindex = ifindex; -+ -+ ipv6_addr_copy(&cell->daddr, daddr); -+ ipv6_addr_copy(&cell->haddr, haddr); -+ ipv6_addr_copy(&cell->coa, coa); -+ ipv6_addr_copy(&cell->rep_coa, rep_coa); -+ -+ /* Convert cell->ll_haddr to Link Local address */ -+ if (flags & MIPV6_BU_F_LLADDR) -+ mipv6_generate_ll_addr(&cell->ll_haddr, haddr); -+ else -+ ipv6_addr_copy(&cell->ll_haddr, haddr); -+ -+ cell->ba_lifetime = ba_lifetime; -+ cell->sequence = sequence; -+ cell->bu_flags = flags; -+} -+ -+/* -+ * Top level DAD routine for performing DAD. -+ * -+ * Return values -+ * 0 : Don't need to do DAD. -+ * 1 : Need to do DAD. -+ * -n : Error, where 'n' is the reason for the error. -+ * -+ * Assumption : DAD process has been optimized by using cached values upto -+ * some time. However sometimes this can cause problems. Eg. when the first -+ * BU was received, DAD might have failed. Before the second BU arrived, -+ * the node using MN's home address might have stopped using it, but still -+ * we will return DAD_DUPLICATE_ADDRESS based on the first DAD's result. Or -+ * this can go the other way around. However, it is a very small possibility -+ * and thus optimization is turned on by default. It is possible to change -+ * this feature (needs a little code-rewriting in this routine), but -+ * currently DAD result is being cached for performance reasons. -+ */ -+int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 ba_lifetime, __u16 sequence, __u8 flags) -+{ -+ int found; -+ struct mipv6_dad_cell *cell; -+ struct mipv6_bce bc_entry; -+ -+ if (ifp->idev->cnf.dad_transmits == 0) { -+ /* DAD is not configured on the HA, return SUCCESS */ -+ return 0; -+ } -+ -+ if (mipv6_bcache_get(haddr, daddr, &bc_entry) == 0) { -+ /* -+ * We already have an entry in our cache - don't need to -+ * do DAD as we are already defending this home address. -+ */ -+ return 0; -+ } -+ -+ write_lock(&dad_lock); -+ if ((cell = mipv6_dad_get_cell(haddr)) != NULL) { -+ /* -+ * An existing entry for BU was found in our cache due -+ * to retransmission of the BU or a new COA registration. -+ */ -+ switch (cell->flags) { -+ case DAD_INIT_ENTRY: -+ /* Old entry is waiting for DAD to complete */ -+ break; -+ case DAD_UNIQUE_ADDRESS: -+ /* DAD is finished successfully - return success. */ -+ write_unlock(&dad_lock); -+ return 0; -+ case DAD_DUPLICATE_ADDRESS: -+ /* -+ * DAD is finished and we got a NA while doing BU - -+ * return failure. -+ */ -+ write_unlock(&dad_lock); -+ return -DUPLICATE_ADDR_DETECT_FAIL; -+ default: -+ /* Unknown state - should never happen */ -+ DEBUG(DBG_WARNING, -+ "cell entry in unknown state : %d", -+ cell->flags); -+ write_unlock(&dad_lock); -+ return -REASON_UNSPECIFIED; -+ } -+ found = 1; -+ } else { -+ if ((cell = (struct mipv6_dad_cell *) -+ kmalloc(sizeof(struct mipv6_dad_cell), GFP_ATOMIC)) -+ == NULL) { -+ return -INSUFFICIENT_RESOURCES; -+ } -+ found = 0; -+ } -+ -+ mipv6_dad_save_cell(cell, ifp, ifindex, daddr, haddr, coa, rep_coa, -+ ba_lifetime, sequence, flags); -+ -+ if (!found) { -+ cell->flags = DAD_INIT_ENTRY; -+ cell->probes = ifp->idev->cnf.dad_transmits; -+ -+ /* Insert element on dad_cell_head list */ -+ dad_cell_head.prev->next = cell; -+ cell->next = &dad_cell_head; -+ cell->prev = dad_cell_head.prev; -+ dad_cell_head.prev = cell; -+ write_unlock(&dad_lock); -+ if (flags & MIPV6_BU_F_LLADDR) { -+ /* join the solicited node MC of the global homeaddr.*/ -+ mipv6_join_sol_mc_addr(&cell->haddr, ifp->idev->dev); -+ /* Send a NS */ -+ mipv6_dad_send_ns(ifp, &cell->haddr); -+ } -+ /* join the solicited node MC of the homeaddr. */ -+ mipv6_join_sol_mc_addr(&cell->ll_haddr, ifp->idev->dev); -+ -+ /* Send a NS */ -+ mipv6_dad_send_ns(ifp, &cell->ll_haddr); -+ -+ /* Initialize timer for this cell to timeout the NS. */ -+ init_timer(&cell->callback_timer); -+ cell->callback_timer.data = (unsigned long) cell; -+ cell->callback_timer.function = mipv6_dad_timeout; -+ cell->callback_timer.expires = jiffies + -+ ifp->idev->nd_parms->retrans_time; -+ add_timer(&cell->callback_timer); -+ } else { -+ write_unlock(&dad_lock); -+ } -+ return 1; -+} -+ -+void __init mipv6_dad_init(void) -+{ -+ dad_cell_head.next = dad_cell_head.prev = &dad_cell_head; -+ init_timer(&dad_cell_head.callback_timer); -+ dad_cell_head.callback_timer.data = 0; -+ dad_cell_head.callback_timer.function = -+ mipv6_dad_delete_old_entries; -+} -+ -+void __exit mipv6_dad_exit(void) -+{ -+ struct mipv6_dad_cell *curr, *next; -+ -+ write_lock_bh(&dad_lock); -+ del_timer(&dad_cell_head.callback_timer); -+ -+ curr = dad_cell_head.next; -+ while (curr != &dad_cell_head) { -+ next = curr->next; -+ del_timer(&curr->callback_timer); -+ if (curr->flags == DAD_INIT_ENTRY) { -+ /* -+ * We were in DAD_INIT state and listening to the -+ * solicited node MC address - need to stop that. -+ */ -+ mipv6_leave_sol_mc_addr(&curr->ll_haddr, -+ curr->ifp->idev->dev); -+ if (ipv6_addr_cmp(&curr->ll_haddr, &curr->haddr)) -+ mipv6_leave_sol_mc_addr(&curr->haddr, -+ curr->ifp->idev->dev); -+ } -+ in6_ifa_put(curr->ifp); -+ kfree(curr); -+ curr = next; -+ } -+ dad_cell_head.next = dad_cell_head.prev = &dad_cell_head; -+ write_unlock_bh(&dad_lock); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.c linux-2.4.25/net/ipv6/mobile_ip6/prefix.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/prefix.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,217 @@ -+/** -+ * Prefix solicitation and advertisement -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/icmpv6.h> -+#include <linux/net.h> -+#include <linux/spinlock.h> -+#include <linux/timer.h> -+#include <linux/netdevice.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "mipv6_icmp.h" -+#include "debug.h" -+#include "sortedlist.h" -+#include "prefix.h" -+#include "config.h" -+ -+#define INFINITY 0xffffffff -+ -+struct timer_list pfx_timer; -+ -+struct list_head pfx_list; -+rwlock_t pfx_list_lock = RW_LOCK_UNLOCKED; -+ -+int compare_pfx_list_entry(const void *data1, const void *data2, -+ int datalen) -+{ -+ struct pfx_list_entry *e1 = (struct pfx_list_entry *) data1; -+ struct pfx_list_entry *e2 = (struct pfx_list_entry *) data2; -+ -+ return ((ipv6_addr_cmp(&e1->daddr, &e2->daddr) == 0) -+ && (e2->ifindex == -1 || e1->ifindex == e2->ifindex)); -+} -+ -+/** -+ * mipv6_pfx_cancel_send - cancel pending items to daddr from saddr -+ * @daddr: Destination address -+ * @ifindex: pending items on this interface will be canceled -+ * -+ * if ifindex == -1, all items to daddr will be removed -+ */ -+void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex) -+{ -+ unsigned long tmp; -+ struct pfx_list_entry entry; -+ -+ DEBUG_FUNC(); -+ -+ /* We'll just be comparing these parts... */ -+ memcpy(&entry.daddr, daddr, sizeof(struct in6_addr)); -+ entry.ifindex = ifindex; -+ -+ write_lock_bh(&pfx_list_lock); -+ -+ while (mipv6_slist_del_item(&pfx_list, &entry, -+ compare_pfx_list_entry) == 0) -+ ; -+ -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ -+ write_unlock_bh(&pfx_list_lock); -+} -+ -+/** -+ * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to -+ * @daddr: address of HA -+ * @saddr: our address to use as source address -+ * @ifindex: interface index -+ */ -+void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr, -+ int ifindex) -+{ -+ unsigned long tmp; -+ struct pfx_list_entry entry; -+ -+ DEBUG_FUNC(); -+ -+ memcpy(&entry.daddr, daddr, sizeof(struct in6_addr)); -+ memcpy(&entry.saddr, saddr, sizeof(struct in6_addr)); -+ entry.retries = 0; -+ entry.ifindex = ifindex; -+ -+ write_lock_bh(&pfx_list_lock); -+ if (mipv6_slist_modify(&pfx_list, &entry, sizeof(struct pfx_list_entry), -+ jiffies + INITIAL_SOLICIT_TIMER * HZ, -+ compare_pfx_list_entry)) -+ DEBUG(DBG_WARNING, "Cannot add new HA to pfx list"); -+ -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ write_unlock_bh(&pfx_list_lock); -+} -+ -+int mipv6_pfx_add_home(int ifindex, struct in6_addr *saddr, -+ struct in6_addr *daddr, unsigned long min_expire) -+{ -+ unsigned long tmp; -+ -+ write_lock(&pfx_list_lock); -+ -+ if (min_expire != INFINITY) { -+ unsigned long expire; -+ struct pfx_list_entry entry; -+ -+ memcpy(&entry.daddr, saddr, sizeof(struct in6_addr)); -+ memcpy(&entry.saddr, daddr, sizeof(struct in6_addr)); -+ entry.retries = 0; -+ entry.ifindex = ifindex; -+ -+ /* This is against the RFC 3775, but we need to set -+ * a minimum interval for a prefix solicitation. -+ * Otherwise a prefix solicitation storm will -+ * result if valid lifetime of the prefix is -+ * smaller than MAX_PFX_ADV_DELAY -+ */ -+ min_expire -= MAX_PFX_ADV_DELAY; -+ min_expire = min_expire < MIN_PFX_SOL_DELAY ? MIN_PFX_SOL_DELAY : min_expire; -+ -+ expire = jiffies + min_expire * HZ; -+ -+ if (mipv6_slist_modify(&pfx_list, &entry, -+ sizeof(struct pfx_list_entry), -+ expire, -+ compare_pfx_list_entry) != 0) -+ DEBUG(DBG_WARNING, "Cannot add new entry to pfx_list"); -+ } -+ -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ -+ write_unlock(&pfx_list_lock); -+ -+ return 0; -+} -+ -+/** -+ * set_ha_pfx_list - manipulate pfx_list for HA when timer goes off -+ * @entry: pfx_list_entry that is due -+ */ -+static void set_ha_pfx_list(struct pfx_list_entry *entry) -+{ -+} -+ -+/** -+ * set_mn_pfx_list - manipulate pfx_list for MN when timer goes off -+ * @entry: pfx_list_entry that is due -+ */ -+static void set_mn_pfx_list(struct pfx_list_entry *entry) -+{ -+} -+ -+/** -+ * pfx_timer_handler - general timer handler -+ * @dummy: dummy -+ * -+ * calls set_ha_pfx_list and set_mn_pfx_list to do the thing when -+ * a timer goes off -+ */ -+static void pfx_timer_handler(unsigned long dummy) -+{ -+ unsigned long tmp; -+ struct pfx_list_entry *entry; -+ -+ DEBUG_FUNC(); -+ -+ write_lock(&pfx_list_lock); -+ if (!(entry = mipv6_slist_get_first(&pfx_list))) -+ goto out; -+ -+ if (mip6node_cnf.capabilities & CAP_HA) -+ set_ha_pfx_list(entry); -+ if (mip6node_cnf.capabilities & CAP_MN) -+ set_mn_pfx_list(entry); -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ -+ out: -+ write_unlock(&pfx_list_lock); -+} -+ -+int mipv6_initialize_pfx_icmpv6(void) -+{ -+ INIT_LIST_HEAD(&pfx_list); -+ -+ init_timer(&pfx_timer); -+ pfx_timer.function = pfx_timer_handler; -+ -+ return 0; -+} -+ -+void mipv6_shutdown_pfx_icmpv6(void) -+{ -+ struct prefix_info *tmp; -+ -+ if (timer_pending(&pfx_timer)) -+ del_timer(&pfx_timer); -+ -+ write_lock_bh(&pfx_list_lock); -+ while ((tmp = mipv6_slist_del_first(&pfx_list))) -+ kfree(tmp); -+ write_unlock_bh(&pfx_list_lock); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.h linux-2.4.25/net/ipv6/mobile_ip6/prefix.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/prefix.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,57 @@ -+/* -+ * MIPL Mobile IPv6 Prefix solicitation and advertisement -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _PREFIX_H -+#define _PREFIX_H -+ -+#include <net/addrconf.h> -+ -+struct pfx_list_entry { -+ struct in6_addr daddr; -+ struct in6_addr saddr; -+ int retries; -+ int ifindex; -+}; -+ -+extern struct list_head pfx_list; -+extern rwlock_t pfx_list_lock; -+extern struct timer_list pfx_timer; -+ -+int compare_pfx_list_entry(const void *data1, const void *data2, -+ int datalen); -+ -+/** -+ * mipv6_pfx_cancel_send - cancel pending pfx_advs/sols to daddr -+ * @daddr: destination address -+ * @ifindex: pending items on this interface will be canceled -+ * -+ * if ifindex == -1, all items to daddr will be removed -+ */ -+void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex); -+ -+/** -+ * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to -+ * @daddr: address of HA -+ * @saddr: our address to use as source address -+ * @ifindex: interface index -+ */ -+void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr, -+ int ifindex); -+ -+void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex); -+ -+int mipv6_pfx_add_home(int ifindex, struct in6_addr *daddr, -+ struct in6_addr *saddr, unsigned long min_expire); -+ -+int mipv6_initialize_pfx_icmpv6(void); -+void mipv6_shutdown_pfx_icmpv6(void); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/prefix_ha.c linux-2.4.25/net/ipv6/mobile_ip6/prefix_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/prefix_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/prefix_ha.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,122 @@ -+/** -+ * Prefix advertisement for Home Agent -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/icmpv6.h> -+#include <linux/net.h> -+#include <linux/spinlock.h> -+#include <linux/timer.h> -+#include <linux/netdevice.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "mipv6_icmp.h" -+#include "debug.h" -+#include "sortedlist.h" -+#include "util.h" -+#include "bcache.h" -+#include "config.h" -+#include "prefix.h" -+ -+/** -+ * pfx_adv_iterator - modify pfx_list entries according to new prefix info -+ * @data: MN's home registration bcache_entry -+ * @args: new prefix info -+ * @sortkey: ignored -+ */ -+static int pfx_adv_iterator(void *data, void *args, unsigned long sortkey) -+{ -+ struct mipv6_bce *bc_entry = (struct mipv6_bce *) data; -+ struct prefix_info *pinfo = (struct prefix_info *) args; -+ -+ if (mipv6_prefix_compare(&bc_entry->coa, &pinfo->prefix, -+ pinfo->prefix_len) == 0) { -+ struct pfx_list_entry pfx_entry; -+ -+ memcpy(&pfx_entry.daddr, &bc_entry->coa, -+ sizeof(struct in6_addr)); -+ memcpy(&pfx_entry.daddr, &bc_entry->our_addr, -+ sizeof(struct in6_addr)); -+ pfx_entry.retries = 0; -+ pfx_entry.ifindex = bc_entry->ifindex; -+ -+ mipv6_slist_modify(&pfx_list, &pfx_entry, -+ sizeof(struct pfx_list_entry), -+ jiffies + -+ net_random() % (MAX_PFX_ADV_DELAY * HZ), -+ compare_pfx_list_entry); -+ } -+ -+ return 0; -+} -+ -+struct homereg_iterator_args { -+ struct list_head *head; -+ int count; -+}; -+ -+static int homereg_iterator(void *data, void *args, unsigned long *sortkey) -+{ -+ struct mipv6_bce *entry = (struct mipv6_bce *) data; -+ struct homereg_iterator_args *state = -+ (struct homereg_iterator_args *) args; -+ -+ if (entry->type == HOME_REGISTRATION) { -+ mipv6_slist_add(state->head, entry, -+ sizeof(struct mipv6_bce), -+ state->count); -+ state->count++; -+ } -+ return 0; -+} -+ -+static int mipv6_bcache_get_homeregs(struct list_head *head) -+{ -+ struct homereg_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.count = 0; -+ args.head = head; -+ -+ mipv6_bcache_iterate(homereg_iterator, &args); -+ return args.count; -+} -+ -+/** -+ * mipv6_prefix_added - prefix was added to interface, act accordingly -+ * @pinfo: prefix_info that was added -+ * @ifindex: interface index -+ */ -+void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex) -+{ -+ int count; -+ unsigned long tmp; -+ struct list_head home_regs; -+ -+ DEBUG_FUNC(); -+ -+ INIT_LIST_HEAD(&home_regs); -+ -+ if (!(count = mipv6_bcache_get_homeregs(&home_regs))) -+ return; -+ -+ write_lock_bh(&pfx_list_lock); -+ mipv6_slist_for_each(&home_regs, pinfo, pfx_adv_iterator); -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ write_unlock_bh(&pfx_list_lock); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.c linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,255 @@ -+/* -+ * rr_cookie.c - Mobile IPv6 return routability crypto -+ * Author : Henrik Petander <henrik.petander@hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * -+ * -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/spinlock.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/random.h> -+ -+#include <net/ipv6.h> -+ -+#include "debug.h" -+#include "hmac.h" -+#include "rr_crypto.h" -+ -+#define DBG_RR 5 -+ -+u8 k_CN[HMAC_SHA1_KEY_SIZE]; // secret key of CN -+ -+u16 curr_index = 0; -+ -+struct nonce_timestamp nonce_table[MAX_NONCES]; -+spinlock_t nonce_lock = SPIN_LOCK_UNLOCKED; -+void update_nonces(void); -+ -+/** nonce_is_fresh - whether the nonce was generated recently -+ * -+ * @non_ts : table entry containing the nonce and a timestamp -+ * @interval : if nonce was generated within interval seconds it is fresh -+ * -+ * Returns 1 if the nonce is fresh, 0 otherwise. -+ */ -+static int nonce_is_fresh(struct nonce_timestamp *non_ts, unsigned long interval) -+{ -+ if (time_before(jiffies, non_ts->timestamp + interval * HZ) && !non_ts->invalid) -+ return 1; -+ return 0; -+} -+void mipv6_rr_invalidate_nonce(u16 nonce_ind) -+{ -+ spin_lock_bh(&nonce_lock); -+ if (nonce_ind > MAX_NONCES) { -+ spin_unlock_bh(&nonce_lock); -+ return; -+ } -+ nonce_table[nonce_ind].invalid = 1; -+ spin_unlock_bh(&nonce_lock); -+} -+/* Returns a pointer to a new nonce */ -+struct mipv6_rr_nonce * mipv6_rr_get_new_nonce(void) -+{ -+ struct mipv6_rr_nonce *nce = kmalloc(sizeof(*nce), GFP_ATOMIC); -+ -+ if (!nce) -+ return NULL; -+ // Lock nonces here -+ spin_lock_bh(&nonce_lock); -+ // If nonce is not fresh create new one -+ if (!nonce_is_fresh(&nonce_table[curr_index], MIPV6_RR_NONCE_LIFETIME)) { -+ // increment the last nonce pointer and create new nonce -+ curr_index++; -+ // Wrap around -+ if (curr_index == MAX_NONCES) -+ curr_index = 0; -+ // Get random data to fill the nonce data -+ get_random_bytes(nonce_table[curr_index].nonce.data, MIPV6_RR_NONCE_DATA_LENGTH); -+ // Fill the index field -+ nonce_table[curr_index].nonce.index = curr_index; -+ nonce_table[curr_index].invalid = 0; -+ nonce_table[curr_index].timestamp = jiffies; -+ } -+ spin_unlock_bh(&nonce_lock); -+ memcpy(nce, &nonce_table[curr_index].nonce, sizeof(*nce)); -+ // Unlock nonces -+ return nce; -+} -+/** mipv6_rr_nonce_get_by_index - returns a nonce for index -+ * @nonce_ind : index of the nonce -+ * -+ * Returns a nonce or NULL if the nonce index was invalid or the nonce -+ * for the index was not fresh. -+ */ -+struct mipv6_rr_nonce * mipv6_rr_nonce_get_by_index(u16 nonce_ind) -+{ -+ struct mipv6_rr_nonce *nce = NULL; -+ -+ spin_lock_bh(&nonce_lock); -+ if (nonce_ind >= MAX_NONCES) { -+ DEBUG(DBG_WARNING, "Nonce index field from BU invalid"); -+ -+ /* Here a double of the nonce_lifetime is used for freshness -+ * verification, since the nonces -+ * are not created in response to every initiator packet -+ */ -+ } else if (nonce_is_fresh(&nonce_table[nonce_ind], 2 * MIPV6_RR_NONCE_LIFETIME)) { -+ nce = kmalloc(sizeof(*nce), GFP_ATOMIC); -+ memcpy(nce, &nonce_table[nonce_ind].nonce, sizeof(*nce)); -+ } -+ spin_unlock_bh(&nonce_lock); -+ -+ return nce; -+} -+ -+/* Fills rr test init cookies with random bytes */ -+void mipv6_rr_mn_cookie_create(u8 *cookie) -+{ -+ get_random_bytes(cookie, MIPV6_RR_COOKIE_LENGTH); -+} -+ -+/** mipv6_rr_cookie_create - builds a home or care-of cookie -+ * -+ * @addr : the home or care-of address from HoTI or CoTI -+ * @ckie : memory where the cookie is copied to -+ * @nce : pointer to a nonce used for the calculation, nce is freed during the function -+ * -+ */ -+int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie, -+ u16 nonce_index) -+{ -+ struct ah_processing ah_proc; -+ u8 digest[HMAC_SHA1_HASH_LEN]; -+ struct mipv6_rr_nonce *nce; -+ -+ if ((nce = mipv6_rr_nonce_get_by_index(nonce_index))== NULL) -+ return -1; -+ -+ if (*ckie == NULL && (*ckie = kmalloc(MIPV6_RR_COOKIE_LENGTH, -+ GFP_ATOMIC)) == NULL) { -+ kfree(nce); -+ return -1; -+ } -+ /* Calculate the full hmac-sha1 digest from address and nonce using the secret key of cn */ -+ -+ if (ah_hmac_sha1_init(&ah_proc, k_CN, HMAC_SHA1_KEY_SIZE) < 0) { -+ DEBUG(DBG_ERROR, "Hmac sha1 initialization failed"); -+ kfree(nce); -+ return -1; -+ } -+ -+ ah_hmac_sha1_loop(&ah_proc, addr, sizeof(*addr)); -+ ah_hmac_sha1_loop(&ah_proc, nce->data, MIPV6_RR_NONCE_DATA_LENGTH); -+ ah_hmac_sha1_result(&ah_proc, digest); -+ -+ -+ /* clean up nonce */ -+ kfree(nce); -+ -+ /* Copy first 64 bits of hash target to the cookie */ -+ memcpy(*ckie, digest, MIPV6_RR_COOKIE_LENGTH); -+ return 0; -+} -+ -+/** mipv6_rr_key_calc - creates BU authentication key -+ * -+ * @hoc : Home Cookie -+ * @coc : Care-of Cookie -+ * -+ * Returns BU authentication key of length HMAC_SHA1_KEY_SIZE or NULL in error cases, -+ * caller needs to free the key. -+ */ -+u8 *mipv6_rr_key_calc(u8 *hoc, u8 *coc) -+{ -+ -+ u8 *key_bu = kmalloc(HMAC_SHA1_KEY_SIZE, GFP_ATOMIC); -+ SHA1_CTX c; -+ -+ if (!key_bu) { -+ DEBUG(DBG_CRITICAL, "Memory allocation failed, could nort create BU authentication key"); -+ return NULL; -+ } -+ -+ /* Calculate the key from home and care-of cookies -+ * Kbu = sha1(home_cookie | care-of cookie) -+ * or KBu = sha1(home_cookie), if MN deregisters -+ */ -+ sha1_init(&c); -+ sha1_compute(&c, hoc, MIPV6_RR_COOKIE_LENGTH); -+ if (coc) -+ sha1_compute(&c, coc, MIPV6_RR_COOKIE_LENGTH); -+ sha1_final(&c, key_bu); -+ DEBUG(DBG_RR, "Home and Care-of cookies used for calculating key "); -+ debug_print_buffer(DBG_RR, hoc, MIPV6_RR_COOKIE_LENGTH); -+ if (coc) -+ debug_print_buffer(DBG_RR, coc, MIPV6_RR_COOKIE_LENGTH); -+ -+ return key_bu; -+} -+ -+void mipv6_rr_init(void) -+{ -+ get_random_bytes(k_CN, HMAC_SHA1_KEY_SIZE); -+ memset(nonce_table, 0, MAX_NONCES * sizeof(struct nonce_timestamp)); -+} -+ -+#ifdef TEST_MIPV6_RR_CRYPTO -+void mipv6_test_rr(void) -+{ -+ struct mipv6_rr_nonce *nonce; -+ struct in6_addr a1, a2; -+ int ind1, ind2; -+ u8 *ckie1 = NULL, *ckie2 = NULL; -+ u8 *key_mn = NULL, *key_cn = NULL; -+ mipv6_init_rr(); -+ -+ nonce = mipv6_rr_get_new_nonce(); -+ if (!nonce) { -+ printk("mipv6_rr_get_new_nonce() failed, at 1! \n"); -+ return; -+ } -+ mipv6_rr_cookie_create(&a1, &ckie1, nonce->index); -+ ind1 = nonce->index; -+ kfree(nonce); -+ -+ nonce = mipv6_rr_get_new_nonce(); -+ if (!nonce) { -+ printk("mipv6_rr_get_new_nonce() failed, at 2! \n"); -+ return; -+ } -+ -+ mipv6_rr_cookie_create(&a2, &ckie2, nonce->index); -+ ind2 = nonce->index; -+ key_mn = mipv6_rr_key_calc(ckie1, ckie2); -+ -+ /* Create home and coa cookies based on indices */ -+ mipv6_rr_cookie_create(&a1, &ckie1, ind1); -+ mipv6_rr_cookie_create(&a2, &ckie2, ind2); -+ key_cn = mipv6_rr_key_calc(ckie1, ckie2); -+ if (!key_cn || !key_mn) { -+ printk("creation of secret key failed!\n"); -+ return; -+ } -+ if(memcmp(key_cn, key_mn, HMAC_SHA1_KEY_SIZE)) -+ printk("mipv6_rr_key_calc produced different keys for MN and CN \n"); -+ else -+ printk("mipv6_rr_crypto test OK\n"); -+ kfree(nonce); -+ kfree(key_cn); -+ kfree(key_mn); -+} -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.h linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/rr_crypto.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/rr_crypto.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,72 @@ -+/* -+ * MIPL Mobile IPv6 Return routability crypto prototypes -+ * -+ * $Id:$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _RR_CRYPTO -+#define _RR_CRYPTO -+ -+#include <linux/in6.h> -+ -+/* Macros and data structures */ -+ -+#define MIPV6_RR_NONCE_LIFETIME 60 -+#define MIPV6_RR_NONCE_DATA_LENGTH 8 -+#define MIPV6_RR_COOKIE_LENGTH 8 -+#define COOKIE_SIZE 8 -+#define MAX_NONCES 4 -+#define HMAC_SHA1_KEY_SIZE 20 -+ -+struct mipv6_rr_nonce { -+ u_int16_t index; -+ u_int8_t data[MIPV6_RR_NONCE_DATA_LENGTH]; -+}; -+ -+struct nonce_timestamp { -+ struct mipv6_rr_nonce nonce; -+ unsigned long timestamp; -+ u_int8_t invalid; -+}; -+ -+/* Function definitions */ -+ -+/* Return 1 if equal, 0 if not */ -+static __inline__ int mipv6_equal_cookies(u8 *c1, u8 *c2) -+{ -+ return (memcmp(c1, c2, MIPV6_RR_COOKIE_LENGTH) == 0); -+} -+ -+/* Function declarations */ -+ -+/* Create cookie for HoTi and CoTi */ -+extern void mipv6_rr_mn_cookie_create(u8 *cookie); -+ -+/* Create cookie for HoT and CoT */ -+extern int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie, u16 nonce_index); -+ -+/* Calculate return routability key from home and care-of cookies, key length is -+ * HMAC_SHA1_KEY_SIZE -+ */ -+extern u_int8_t *mipv6_rr_key_calc(u8 *hoc, u8 *coc); -+ -+extern struct mipv6_rr_nonce *mipv6_rr_get_new_nonce(void); -+ -+/* For avoiding replay attacks when MN deregisters */ -+extern void mipv6_rr_invalidate_nonce(u16 nonce_index); -+/* -+ * initializes the return routability crypto -+ */ -+ -+void mipv6_rr_init(void); -+ -+#ifdef TEST_MIPV6_RR_CRYPTO -+void mipv6_test_rr(void); -+#endif /* TEST_MIPV6_RR_CRYPTO */ -+ -+#endif /* RR_CRYPTO */ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.c linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.c 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,349 @@ -+/** -+ * Sorted list - linked list with sortkey. -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/list.h> -+#include <linux/slab.h> -+#include <linux/spinlock.h> -+#include <linux/string.h> -+ -+struct mipv6_sorted_list_entry { -+ struct list_head list; -+ void *data; -+ int datalen; -+ unsigned long sortkey; -+}; -+ -+/** -+ * compare - compares two arbitrary data items -+ * @data1: first data item -+ * @data2: second data item -+ * @datalen: length of data items in bits -+ * -+ * datalen is in bits! -+ */ -+int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen) -+{ -+ int n = datalen; -+ __u8 * ptr1 = (__u8 *)data1; -+ __u8 * ptr2 = (__u8 *)data2; -+ -+ for (; n>=0; n-=8, ptr1++, ptr2++) { -+ if (n >= 8) { -+ if (*ptr1 != *ptr2) -+ return 0; -+ } else { -+ if ((*ptr1 ^ *ptr2) & ((~0) << (8 - n))) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+/** -+ * mipv6_slist_add - add an entry to sorted list -+ * @head: list_head of the sorted list -+ * @data: item to store -+ * @datalen: length of data (in bytes) -+ * @key: sortkey of item -+ * -+ * Allocates memory for entry and data -+ */ -+int mipv6_slist_add(struct list_head *head, void *data, int datalen, -+ unsigned long sortkey) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry, *tmp, *next; -+ -+ entry = kmalloc(sizeof(struct mipv6_sorted_list_entry), GFP_ATOMIC); -+ -+ if (!entry) -+ return -1; -+ -+ entry->data = kmalloc(datalen, GFP_ATOMIC); -+ -+ if (!entry->data) { -+ kfree(entry); -+ return -1; -+ } -+ -+ memcpy(entry->data, data, datalen); -+ entry->datalen = datalen; -+ entry->sortkey = sortkey; -+ -+ if ((pos = head->next) == head) { -+ list_add(&entry->list, head); -+ return 0; -+ } -+ -+ tmp = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (entry->sortkey < tmp->sortkey) { -+ list_add(&entry->list, head); -+ return 0; -+ } -+ -+ for (; pos != head; pos = pos->next) { -+ tmp = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (pos->next == head) { -+ list_add(&entry->list, &tmp->list); -+ return 0; -+ } -+ next = list_entry(pos->next, struct mipv6_sorted_list_entry, list); -+ if (entry->sortkey >= tmp->sortkey && entry->sortkey < next->sortkey) { -+ list_add(&entry->list, &tmp->list); -+ return 0; -+ } -+ } -+ -+ /* never reached */ -+ return -1; -+} -+ -+/** -+ * mipv6_slist_get_first - get the first data item in the list -+ * @head: list_head of the sorted list -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_first(struct list_head *head) -+{ -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return NULL; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ return entry->data; -+} -+ -+/** -+ * mipv6_slist_del_first - delete (and get) the first item in list -+ * @head: list_head of the sorted list -+ * -+ * Remember to kfree the item -+ */ -+void *mipv6_slist_del_first(struct list_head *head) -+{ -+ void *tmp; -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return NULL; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ tmp = entry->data; -+ -+ list_del(head->next); -+ kfree(entry); -+ -+ return tmp; -+} -+ -+/** -+ * mipv6_slist_del_item - delete entry -+ * @head: list_head of the sorted list -+ * @data: item to delete -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) -+ */ -+int mipv6_slist_del_item(struct list_head *head, void *data, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ for(pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (compare(data, entry->data, entry->datalen)) { -+ list_del(pos); -+ kfree(entry->data); -+ kfree(entry); -+ return 0; -+ } -+ } -+ -+ return -1; -+} -+ -+/** -+ * mipv6_slist_get_first_key - get sortkey of the first item -+ * @head: list_head of the sorted list -+ */ -+unsigned long mipv6_slist_get_first_key(struct list_head *head) -+{ -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return 0; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ return entry->sortkey; -+} -+ -+/** -+ * mipv6_slist_get_key - get sortkey of the data item -+ * @head: list_head of the sorted list -+ * @data: the item to search for -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) -+ */ -+unsigned long mipv6_slist_get_key(struct list_head *head, void *data, -+ int (*compare)(const void *data1, -+ const void *data2, -+ int datalen)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ for(pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (compare(data, entry->data, entry->datalen)) -+ return entry->sortkey; -+ } -+ -+ return 0; -+} -+ -+/** -+ * mipv6_slist_get_data - get the data item identified by sortkey -+ * @head: list_head of the sorted list -+ * @key: sortkey of the item -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ list_for_each(pos, head) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (entry->sortkey == sortkey) -+ return entry->data; -+ } -+ -+ return NULL; -+} -+ -+/** -+ * reorder_entry - move an entry to a new position according to sortkey -+ * @head: list_head of the sorted list -+ * @entry_pos: current place of the entry -+ * @key: new sortkey -+ */ -+static void reorder_entry(struct list_head *head, struct list_head *entry_pos, -+ unsigned long sortkey) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ list_del(entry_pos); -+ -+ for (pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (sortkey >= entry->sortkey) { -+ list_add(entry_pos, &entry->list); -+ return; -+ } -+ } -+ -+ list_add(entry_pos, head); -+} -+ -+/** -+ * mipv6_slist_modify - modify data item -+ * @head: list_head of the sorted list -+ * @data: item, whose sortkey is to be modified -+ * @datalen: datalen in bytes -+ * @new_key: new sortkey -+ * @compare: function used for comparing the data items -+ * -+ * Compies the new data on top of the old one, if compare function returns -+ * true. If there's no matching entry, new one will be created. -+ * Compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) -+ */ -+int mipv6_slist_modify(struct list_head *head, void *data, int datalen, -+ unsigned long new_key, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ for (pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (compare(data, entry->data, datalen)) { -+ memcpy(entry->data, data, datalen); -+ entry->sortkey = new_key; -+ reorder_entry(head, &entry->list, new_key); -+ return 0; -+ } -+ } -+ -+ return mipv6_slist_add(head, data, datalen, new_key); -+} -+ -+/** -+ * mipv6_slist_push_first - move the first entry to place indicated by new_key -+ * @head: list_head of the sorted list -+ * @new_key: new sortkey -+ */ -+int mipv6_slist_push_first(struct list_head *head, unsigned long new_key) -+{ -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return -1; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ entry->sortkey = new_key; -+ -+ reorder_entry(head, head->next, new_key); -+ return 0; -+} -+ -+/** -+ * mipv6_slist_for_each - apply func to every item in list -+ * @head: list_head of the sorted list -+ * @args: args to pass to func -+ * @func: function to use -+ * -+ * function must be of type -+ * int (*func)(void *data, void *args, unsigned long sortkey) -+ * List iteration will stop once func has been applied to every item -+ * or when func returns true -+ */ -+int mipv6_slist_for_each(struct list_head *head, void *args, -+ int (*func)(void *data, void *args, -+ unsigned long sortkey)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ list_for_each(pos, head) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (func(entry->data, args, entry->sortkey)) -+ break; -+ } -+ -+ return 0; -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.h linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/sortedlist.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/sortedlist.h 2004-06-26 11:29:31.000000000 +0100 -@@ -0,0 +1,133 @@ -+/* -+ * Sorted list - linked list with sortkey -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/** -+ * compare - compares two arbitrary data items -+ * @data1: first data item -+ * @data2: second data item -+ * @datalen: length of data items in bits -+ * -+ * datalen is in bits! -+ */ -+int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen); -+ -+/** -+ * mipv6_slist_add - add an entry to sorted list -+ * @head: list_head of the sorted list -+ * @data: item to store -+ * @datalen: length of data (in bytes) -+ * @key: sortkey of item -+ * -+ * Allocates memory for entry and data -+ */ -+int mipv6_slist_add(struct list_head *head, void *data, int datalen, -+ unsigned long sortkey); -+ -+/** -+ * mipv6_slist_get_first - get the first data item in the list -+ * @head: list_head of the sorted list -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_first(struct list_head *head); -+ -+/** -+ * mipv6_slist_del_first - delete (and get) the first item in list -+ * @head: list_head of the sorted list -+ * -+ * Remember to kfree the item -+ */ -+void *mipv6_slist_del_first(struct list_head *head); -+ -+/** -+ * mipv6_slist_del_item - delete entry -+ * @head: list_head of the sorted list -+ * @data: item to delete -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits -+ */ -+int mipv6_slist_del_item(struct list_head *head, void *data, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_get_first_key - get sortkey of the first item -+ * @head: list_head of the sorted list -+ */ -+unsigned long mipv6_slist_get_first_key(struct list_head *head); -+ -+/** -+ * mipv6_slist_get_key - get sortkey of the data item -+ * @head: list_head of the sorted list -+ * @data: the item to search for -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits -+ */ -+unsigned long mipv6_slist_get_key(struct list_head *head, void *data, -+ int (*compare)(const void *data1, -+ const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_get_data - get the data item identified by sortkey -+ * @head: list_head of the sorted list -+ * @key: sortkey of the item -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey); -+ -+/** -+ * mipv6_slist_modify - modify data item -+ * @head: list_head of the sorted list -+ * @data: item, whose sortkey is to be modified -+ * @datalen: datalen in bytes -+ * @new_key: new sortkey -+ * @compare: function used for comparing the data items -+ * -+ * Compies the new data on top of the old one, if compare function returns -+ * non-negative. If there's no matching entry, new one will be created. -+ * Compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits. -+ */ -+int mipv6_slist_modify(struct list_head *head, void *data, int datalen, -+ unsigned long new_key, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_push_first - move the first entry to place indicated by new_key -+ * @head: list_head of the sorted list -+ * @new_key: new sortkey -+ */ -+int mipv6_slist_push_first(struct list_head *head, unsigned long new_key); -+ -+/** -+ * mipv6_slist_for_each - apply func to every item in list -+ * @head: list_head of the sorted list -+ * @args: args to pass to func -+ * @func: function to use -+ * -+ * function must be of type -+ * int (*func)(void *data, void *args, unsigned long sortkey) -+ * List iteration will stop once func has been applied to every item -+ * or when func returns true -+ */ -+int mipv6_slist_for_each(struct list_head *head, void *args, -+ int (*func)(void *data, void *args, -+ unsigned long sortkey)); -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/stats.c linux-2.4.25/net/ipv6/mobile_ip6/stats.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/stats.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/stats.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,90 @@ -+/* -+ * Statistics module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Changes: -+ * Krishna Kumar, -+ * Venkata Jagana : SMP locking fix -+ */ -+ -+#include <linux/config.h> -+#include <linux/proc_fs.h> -+#include "stats.h" -+ -+struct mipv6_statistics mipv6_stats; -+ -+static int proc_info_dump( -+ char *buffer, char **start, -+ off_t offset, int length) -+{ -+ struct inf { -+ char *name; -+ int *value; -+ } int_stats[] = { -+ {"NEncapsulations", &mipv6_stats.n_encapsulations}, -+ {"NDecapsulations", &mipv6_stats.n_decapsulations}, -+ {"NBindRefreshRqsRcvd", &mipv6_stats.n_brr_rcvd}, -+ {"NHomeTestInitsRcvd", &mipv6_stats.n_hoti_rcvd}, -+ {"NCareofTestInitsRcvd", &mipv6_stats.n_coti_rcvd}, -+ {"NHomeTestRcvd", &mipv6_stats.n_hot_rcvd}, -+ {"NCareofTestRcvd", &mipv6_stats.n_cot_rcvd}, -+ {"NBindUpdatesRcvd", &mipv6_stats.n_bu_rcvd}, -+ {"NBindAcksRcvd", &mipv6_stats.n_ba_rcvd}, -+ {"NBindNAcksRcvd", &mipv6_stats.n_ban_rcvd}, -+ {"NBindErrorsRcvd", &mipv6_stats.n_be_rcvd}, -+ {"NBindRefreshRqsSent", &mipv6_stats.n_brr_sent}, -+ {"NHomeTestInitsSent", &mipv6_stats.n_hoti_sent}, -+ {"NCareofTestInitsSent", &mipv6_stats.n_coti_sent}, -+ {"NHomeTestSent", &mipv6_stats.n_hot_sent}, -+ {"NCareofTestSent", &mipv6_stats.n_cot_sent}, -+ {"NBindUpdatesSent", &mipv6_stats.n_bu_sent}, -+ {"NBindAcksSent", &mipv6_stats.n_ba_sent}, -+ {"NBindNAcksSent", &mipv6_stats.n_ban_sent}, -+ {"NBindErrorsSent", &mipv6_stats.n_be_sent}, -+ {"NBindUpdatesDropAuth", &mipv6_stats.n_bu_drop.auth}, -+ {"NBindUpdatesDropInvalid", &mipv6_stats.n_bu_drop.invalid}, -+ {"NBindUpdatesDropMisc", &mipv6_stats.n_bu_drop.misc}, -+ {"NBindAcksDropAuth", &mipv6_stats.n_bu_drop.auth}, -+ {"NBindAcksDropInvalid", &mipv6_stats.n_bu_drop.invalid}, -+ {"NBindAcksDropMisc", &mipv6_stats.n_bu_drop.misc}, -+ {"NBindRqsDropAuth", &mipv6_stats.n_bu_drop.auth}, -+ {"NBindRqsDropInvalid", &mipv6_stats.n_bu_drop.invalid}, -+ {"NBindRqsDropMisc", &mipv6_stats.n_bu_drop.misc} -+ }; -+ -+ int i; -+ int len = 0; -+ for(i=0; i<sizeof(int_stats) / sizeof(struct inf); i++) { -+ len += sprintf(buffer + len, "%s = %d\n", -+ int_stats[i].name, *int_stats[i].value); -+ } -+ -+ *start = buffer + offset; -+ -+ len -= offset; -+ -+ if(len > length) len = length; -+ -+ return len; -+} -+ -+int mipv6_stats_init(void) -+{ -+ memset(&mipv6_stats, 0, sizeof(struct mipv6_statistics)); -+ proc_net_create("mip6_stat", 0, proc_info_dump); -+ return 0; -+} -+ -+void mipv6_stats_exit(void) -+{ -+ proc_net_remove("mip6_stat"); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/stats.h linux-2.4.25/net/ipv6/mobile_ip6/stats.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/stats.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/stats.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,71 @@ -+/* -+ * MIPL Mobile IPv6 Statistics header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _STATS_H -+#define _STATS_H -+ -+struct mipv6_drop { -+ __u32 auth; -+ __u32 invalid; -+ __u32 misc; -+}; -+ -+struct mipv6_statistics { -+ int n_encapsulations; -+ int n_decapsulations; -+ int n_mh_in_msg; -+ int n_mh_in_error; -+ int n_mh_out_msg; -+ int n_mh_out_error; -+ -+ int n_brr_rcvd; -+ int n_hoti_rcvd; -+ int n_coti_rcvd; -+ int n_hot_rcvd; -+ int n_cot_rcvd; -+ int n_bu_rcvd; -+ int n_ba_rcvd; -+ int n_ban_rcvd; -+ int n_be_rcvd; -+ -+ int n_brr_sent; -+ int n_hoti_sent; -+ int n_coti_sent; -+ int n_hot_sent; -+ int n_cot_sent; -+ int n_bu_sent; -+ int n_ba_sent; -+ int n_ban_sent; -+ int n_be_sent; -+ -+ int n_ha_rcvd; -+ int n_ha_sent; -+ -+ struct mipv6_drop n_bu_drop; -+ struct mipv6_drop n_ba_drop; -+ struct mipv6_drop n_brr_drop; -+ struct mipv6_drop n_be_drop; -+ struct mipv6_drop n_ha_drop; -+}; -+ -+extern struct mipv6_statistics mipv6_stats; -+ -+#ifdef CONFIG_SMP -+/* atomic_t is max 24 bits long */ -+#define MIPV6_INC_STATS(X) atomic_inc((atomic_t *)&mipv6_stats.X); -+#else -+#define MIPV6_INC_STATS(X) mipv6_stats.X++; -+#endif -+ -+int mipv6_stats_init(void); -+void mipv6_stats_exit(void); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,35 @@ -+/* -+ * MIPL Mobile IPv6 IP6-IP6 tunneling header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _TUNNEL_H -+#define _TUNNEL_H -+ -+#include <linux/in6.h> -+#include <linux/if_arp.h> -+#include <net/ipv6_tunnel.h> -+ -+static __inline__ int is_mip6_tnl(struct ip6_tnl *t) -+{ -+ return (t != NULL && -+ t->parms.flags & IP6_TNL_F_KERNEL_DEV && -+ t->parms.flags & IP6_TNL_F_MIP6_DEV); -+ -+} -+ -+static __inline__ int dev_is_mip6_tnl(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *)dev->priv; -+ return (dev->type == ARPHRD_TUNNEL6 && is_mip6_tnl(t)); -+} -+ -+ -+#endif -+ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.c linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,264 @@ -+/* -+ * IPv6-IPv6 tunneling module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Ville Nuorvala <vnuorval@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/net.h> -+#include <linux/skbuff.h> -+#include <linux/ipv6.h> -+#include <linux/net.h> -+#include <linux/netdevice.h> -+#include <linux/init.h> -+#include <linux/route.h> -+#include <linux/ipv6_route.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/protocol.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/dst.h> -+#include <net/addrconf.h> -+ -+#include "tunnel.h" -+#include "debug.h" -+#include "stats.h" -+#include "config.h" -+ -+#define MIPV6_TNL_MAX IP6_TNL_MAX -+#define MIPV6_TNL_MIN 1 -+ -+int mipv6_max_tnls = 3; -+int mipv6_min_tnls = 1; -+ -+DECLARE_MUTEX(tnl_sem); -+ -+int mipv6_max_tnls_sysctl(ctl_table *ctl, int write, struct file *filp, -+ void *buffer, size_t *lenp) -+{ -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ down(&tnl_sem); -+ if (write) { -+ int diff; -+ int old_max_tnls = mipv6_max_tnls; -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ if (err < 0) -+ goto out; -+ if (mipv6_max_tnls < mipv6_min_tnls || -+ mipv6_max_tnls > MIPV6_TNL_MAX) { -+ mipv6_max_tnls = old_max_tnls; -+ goto out; -+ } -+ if (mipv6_max_tnls < old_max_tnls) { -+ diff = old_max_tnls - mipv6_max_tnls; -+ ip6ip6_tnl_dec_max_kdev_count(diff); -+ } else if (mipv6_max_tnls > old_max_tnls) { -+ diff = mipv6_max_tnls - old_max_tnls; -+ ip6ip6_tnl_inc_max_kdev_count(diff); -+ } -+ } else { -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ } -+out: -+ up(&tnl_sem); -+ return err; -+} -+ -+int mipv6_min_tnls_sysctl(ctl_table *ctl, int write, struct file *filp, -+ void *buffer, size_t *lenp) -+{ -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ down(&tnl_sem); -+ if (write) { -+ int diff; -+ int old_min_tnls = mipv6_min_tnls; -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ if (err < 0) -+ goto out; -+ if (mipv6_min_tnls > mipv6_max_tnls || -+ mipv6_min_tnls < MIPV6_TNL_MIN) { -+ mipv6_min_tnls = old_min_tnls; -+ goto out; -+ } -+ if (mipv6_min_tnls < old_min_tnls) { -+ diff = old_min_tnls - mipv6_min_tnls; -+ ip6ip6_tnl_dec_min_kdev_count(diff); -+ } else if (mipv6_min_tnls > old_min_tnls) { -+ diff = mipv6_min_tnls - old_min_tnls; -+ ip6ip6_tnl_inc_min_kdev_count(diff); -+ } -+ } else { -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ } -+out: -+ up(&tnl_sem); -+ return err; -+} -+ -+static __inline__ int mipv6_tnl_add(struct in6_addr *remote, -+ struct in6_addr *local) -+{ -+ struct ip6_tnl_parm p; -+ int ret; -+ -+ DEBUG_FUNC(); -+ -+ memset(&p, 0, sizeof(p)); -+ p.proto = IPPROTO_IPV6; -+ ipv6_addr_copy(&p.laddr, local); -+ ipv6_addr_copy(&p.raddr, remote); -+ p.hop_limit = 255; -+ p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | -+ IP6_TNL_F_IGN_ENCAP_LIMIT); -+ -+ ret = ip6ip6_kernel_tnl_add(&p); -+ if (ret > 0) { -+ DEBUG(DBG_INFO, "added tunnel from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(local), NIPV6ADDR(remote)); -+ } else { -+ DEBUG(DBG_WARNING, "unable to add tunnel from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(local), NIPV6ADDR(remote)); -+ } -+ return ret; -+} -+ -+static __inline__ int mipv6_tnl_del(struct in6_addr *remote, -+ struct in6_addr *local) -+{ -+ struct ip6_tnl *t = ip6ip6_tnl_lookup(remote, local); -+ -+ DEBUG_FUNC(); -+ -+ if (t != NULL && (t->parms.flags & IP6_TNL_F_MIP6_DEV)) { -+ DEBUG(DBG_INFO, "deleting tunnel from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(local), NIPV6ADDR(remote)); -+ -+ return ip6ip6_kernel_tnl_del(t); -+ } -+ return 0; -+} -+ -+static int add_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ struct in6_rtmsg rtmsg; -+ int err; -+ struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr); -+ -+ if (!is_mip6_tnl(t)) { -+ DEBUG(DBG_CRITICAL,"Tunnel missing"); -+ return -ENODEV; -+ } -+ -+ DEBUG(DBG_INFO, "adding route to: %x:%x:%x:%x:%x:%x:%x:%x via " -+ "tunnel device", NIPV6ADDR(home_addr)); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr); -+ rtmsg.rtmsg_dst_len = 128; -+ rtmsg.rtmsg_type = RTMSG_NEWROUTE; -+ rtmsg.rtmsg_flags = RTF_UP | RTF_NONEXTHOP | RTF_HOST | RTF_MOBILENODE; -+ rtmsg.rtmsg_ifindex = t->dev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) { -+ err = 0; -+ } -+ return err; -+} -+ -+static void del_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr); -+ -+ DEBUG_FUNC(); -+ -+ if (is_mip6_tnl(t)) { -+ struct in6_rtmsg rtmsg; -+ -+ DEBUG(DBG_INFO, "deleting route to: %x:%x:%x:%x:%x:%x:%x:%x " -+ " via tunnel device", NIPV6ADDR(home_addr)); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr); -+ rtmsg.rtmsg_dst_len = 128; -+ rtmsg.rtmsg_ifindex = t->dev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ ip6_route_del(&rtmsg, NULL); -+ } -+} -+ -+ -+int mipv6_add_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ int ret; -+ -+ DEBUG_FUNC(); -+ -+ ret = mipv6_tnl_add(coa, ha_addr); -+ -+ if (ret > 0) { -+ int err = add_route_to_mn(coa, ha_addr, home_addr); -+ if (err) { -+ if (err != -ENODEV) { -+ mipv6_tnl_del(coa, ha_addr); -+ } -+ return err; -+ } -+ } -+ return ret; -+} -+ -+int mipv6_del_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ DEBUG_FUNC(); -+ del_route_to_mn(coa, ha_addr, home_addr); -+ return mipv6_tnl_del(coa, ha_addr); -+} -+ -+__init void mipv6_initialize_tunnel(void) -+{ -+ down(&tnl_sem); -+ ip6ip6_tnl_inc_max_kdev_count(mipv6_max_tnls); -+ ip6ip6_tnl_inc_min_kdev_count(mipv6_min_tnls); -+ up(&tnl_sem); -+ mip6_fn.bce_tnl_rt_add = add_route_to_mn; -+ mip6_fn.bce_tnl_rt_del = del_route_to_mn; -+} -+ -+__exit void mipv6_shutdown_tunnel(void) -+{ -+ mip6_fn.bce_tnl_rt_del = NULL; -+ mip6_fn.bce_tnl_rt_add = NULL; -+ down(&tnl_sem); -+ ip6ip6_tnl_dec_min_kdev_count(mipv6_min_tnls); -+ ip6ip6_tnl_dec_max_kdev_count(mipv6_max_tnls); -+ up(&tnl_sem); -+} -+ -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_ha.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_ha.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,20 @@ -+#ifndef _TUNNEL_HA_H -+#define _TUNNEL_HA_H -+ -+#include "tunnel.h" -+ -+extern int mipv6_max_tnls; -+extern int mipv6_min_tnls; -+ -+extern void mipv6_initialize_tunnel(void); -+extern void mipv6_shutdown_tunnel(void); -+ -+extern int mipv6_add_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+extern int mipv6_del_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.c linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.c ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.c 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.c 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,160 @@ -+/* -+ * IPv6-IPv6 tunneling module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Ville Nuorvala <vnuorval@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/net.h> -+#include <linux/skbuff.h> -+#include <linux/ipv6.h> -+#include <linux/net.h> -+#include <linux/netdevice.h> -+#include <linux/init.h> -+#include <linux/route.h> -+#include <linux/ipv6_route.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/protocol.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/dst.h> -+#include <net/addrconf.h> -+ -+#include "tunnel.h" -+#include "debug.h" -+#include "stats.h" -+ -+static struct net_device *mn_ha_tdev; -+ -+static spinlock_t mn_ha_lock = SPIN_LOCK_UNLOCKED; -+ -+static __inline__ int add_reverse_route(struct in6_addr *ha_addr, -+ struct in6_addr *home_addr, -+ struct net_device *tdev) -+{ -+ struct in6_rtmsg rtmsg; -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ rtmsg.rtmsg_type = RTMSG_NEWROUTE; -+ ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr); -+ rtmsg.rtmsg_src_len = 128; -+ rtmsg.rtmsg_flags = RTF_UP | RTF_DEFAULT; -+ rtmsg.rtmsg_ifindex = tdev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) { -+ return 0; -+ } -+ return err; -+} -+ -+static __inline__ void del_reverse_route(struct in6_addr *ha_addr, -+ struct in6_addr *home_addr, -+ struct net_device *tdev) -+{ -+ struct in6_rtmsg rtmsg; -+ -+ DEBUG(DBG_INFO, "removing reverse route via tunnel device"); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr); -+ rtmsg.rtmsg_src_len = 128; -+ rtmsg.rtmsg_ifindex = tdev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ ip6_route_del(&rtmsg, NULL); -+} -+ -+int mipv6_add_tnl_to_ha(void) -+{ -+ struct ip6_tnl_parm p; -+ struct ip6_tnl *t; -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ memset(&p, 0, sizeof(p)); -+ p.proto = IPPROTO_IPV6; -+ p.hop_limit = 255; -+ p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | -+ IP6_TNL_F_IGN_ENCAP_LIMIT); -+ strcpy(p.name, "mip6mnha1"); -+ -+ rtnl_lock(); -+ if ((err = ip6ip6_tnl_create(&p, &t))) { -+ rtnl_unlock(); -+ return err; -+ } -+ spin_lock_bh(&mn_ha_lock); -+ -+ if (!mn_ha_tdev) { -+ mn_ha_tdev = t->dev; -+ dev_hold(mn_ha_tdev); -+ } -+ spin_unlock_bh(&mn_ha_lock); -+ dev_open(t->dev); -+ rtnl_unlock(); -+ return 0; -+} -+ -+int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, -+ struct in6_addr *coa, -+ struct in6_addr *home_addr) -+{ -+ int err = -ENODEV; -+ -+ DEBUG_FUNC(); -+ -+ spin_lock_bh(&mn_ha_lock); -+ if (mn_ha_tdev) { -+ struct ip6_tnl_parm p; -+ memset(&p, 0, sizeof(p)); -+ p.proto = IPPROTO_IPV6; -+ ipv6_addr_copy(&p.laddr, coa); -+ ipv6_addr_copy(&p.raddr, ha_addr); -+ p.hop_limit = 255; -+ p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | -+ IP6_TNL_F_IGN_ENCAP_LIMIT); -+ -+ ip6ip6_tnl_change((struct ip6_tnl *) mn_ha_tdev->priv, &p); -+ if (ipv6_addr_cmp(coa, home_addr)) { -+ err = add_reverse_route(ha_addr, home_addr, -+ mn_ha_tdev); -+ } else { -+ del_reverse_route(ha_addr, home_addr, mn_ha_tdev); -+ err = 0; -+ } -+ } -+ spin_unlock_bh(&mn_ha_lock); -+ return err; -+} -+ -+void mipv6_del_tnl_to_ha(void) -+{ -+ struct net_device *dev; -+ -+ DEBUG_FUNC(); -+ -+ rtnl_lock(); -+ spin_lock_bh(&mn_ha_lock); -+ dev = mn_ha_tdev; -+ mn_ha_tdev = NULL; -+ spin_unlock_bh(&mn_ha_lock); -+ dev_put(dev); -+ unregister_netdevice(dev); -+ rtnl_unlock(); -+} -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.h linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/tunnel_mn.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/tunnel_mn.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,14 @@ -+#ifndef _TUNNEL_MN_H -+#define _TUNNEL_MN_H -+ -+#include "tunnel.h" -+ -+extern int mipv6_add_tnl_to_ha(void); -+ -+extern int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, -+ struct in6_addr *coa, -+ struct in6_addr *home_addr); -+ -+extern int mipv6_del_tnl_to_ha(void); -+ -+#endif -diff -uprN linux-2.4.25.old/net/ipv6/mobile_ip6/util.h linux-2.4.25/net/ipv6/mobile_ip6/util.h ---- linux-2.4.25.old/net/ipv6/mobile_ip6/util.h 1970-01-01 01:00:00.000000000 +0100 -+++ linux-2.4.25/net/ipv6/mobile_ip6/util.h 2004-06-26 11:29:32.000000000 +0100 -@@ -0,0 +1,91 @@ -+/* -+ * MIPL Mobile IPv6 Utility functions -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _UTIL_H -+#define _UTIL_H -+ -+#include <linux/in6.h> -+#include <asm/byteorder.h> -+ -+/** -+ * mipv6_prefix_compare - Compare two IPv6 prefixes -+ * @addr: IPv6 address -+ * @prefix: IPv6 address -+ * @nprefix: number of bits to compare -+ * -+ * Perform prefix comparison bitwise for the @nprefix first bits -+ * Returns 1, if the prefixes are the same, 0 otherwise -+ **/ -+static inline int mipv6_prefix_compare(const struct in6_addr *addr, -+ const struct in6_addr *prefix, -+ const unsigned int pfix_len) -+{ -+ int i; -+ unsigned int nprefix = pfix_len; -+ -+ if (nprefix > 128) -+ return 0; -+ -+ for (i = 0; nprefix > 0; nprefix -= 32, i++) { -+ if (nprefix >= 32) { -+ if (addr->s6_addr32[i] != prefix->s6_addr32[i]) -+ return 0; -+ } else { -+ if (((addr->s6_addr32[i] ^ prefix->s6_addr32[i]) & -+ ((~0) << (32 - nprefix))) != 0) -+ return 0; -+ return 1; -+ } -+ } -+ -+ return 1; -+} -+ -+/** -+ * homeagent_anycast - Compute Home Agent anycast address -+ * @ac_addr: append home agent anycast suffix to passed prefix -+ * @prefix: prefix ha anycast address is generated from -+ * @plen: length of prefix in bits -+ * -+ * Calculate corresponding Home Agent Anycast Address (RFC2526) in a -+ * given subnet. -+ */ -+static inline int -+mipv6_ha_anycast(struct in6_addr *ac_addr, struct in6_addr *prefix, int plen) -+{ -+ if (plen <= 0 || plen > 120) { -+ /* error, interface id should be minimum 8 bits */ -+ return -1; -+ } -+ ipv6_addr_copy(ac_addr, prefix); -+ -+ if (plen < 32) -+ ac_addr->s6_addr32[0] |= htonl((u32)(~0) >> plen); -+ if (plen < 64) -+ ac_addr->s6_addr32[1] |= htonl((u32)(~0) >> (plen > 32 ? plen % 32 : 0)); -+ if (plen < 92) -+ ac_addr->s6_addr32[2] |= htonl((u32)(~0) >> (plen > 64 ? plen % 32 : 0)); -+ if (plen <= 120) -+ ac_addr->s6_addr32[3] |= htonl((u32)(~0) >> (plen > 92 ? plen % 32 : 0)); -+ -+ /* RFC2526: for interface identifiers in EUI-64 -+ * format, the universal/local bit in the interface -+ * identifier MUST be set to 0. */ -+ if (plen == 64) { -+ ac_addr->s6_addr32[2] &= (int)htonl(0xfdffffff); -+ } -+ /* Mobile IPv6 Home-Agents anycast id (0x7e) */ -+ ac_addr->s6_addr32[3] &= (int)htonl(0xfffffffe); -+ -+ return 0; -+} -+ -+#endif /* _UTIL_H */ -diff -uprN linux-2.4.25.old/net/ipv6/ndisc.c linux-2.4.25/net/ipv6/ndisc.c ---- linux-2.4.25.old/net/ipv6/ndisc.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/ndisc.c 2004-06-26 11:29:32.000000000 +0100 -@@ -23,6 +23,7 @@ - * and moved to net/core. - * Pekka Savola : RFC2461 validation - * YOSHIFUJI Hideaki @USAGI : Verify ND options properly -+ * Ville Nuorvala : RFC2461 fixes to proxy ND - */ - - /* Set to 3 to get tracing... */ -@@ -70,6 +71,7 @@ - #include <net/ip6_route.h> - #include <net/addrconf.h> - #include <net/icmp.h> -+#include <net/mipglue.h> - - #include <net/checksum.h> - #include <linux/proc_fs.h> -@@ -187,6 +189,8 @@ struct ndisc_options *ndisc_parse_option - case ND_OPT_TARGET_LL_ADDR: - case ND_OPT_MTU: - case ND_OPT_REDIRECT_HDR: -+ case ND_OPT_RTR_ADV_INTERVAL: -+ case ND_OPT_HOME_AGENT_INFO: - if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { - ND_PRINTK2((KERN_WARNING - "ndisc_parse_options(): duplicated ND6 option found: type=%d\n", -@@ -372,8 +376,8 @@ ndisc_build_ll_hdr(struct sk_buff *skb, - */ - - void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, -- struct in6_addr *daddr, struct in6_addr *solicited_addr, -- int router, int solicited, int override, int inc_opt) -+ struct in6_addr *daddr, struct in6_addr *solicited_addr, -+ int router, int solicited, int override, int inc_opt) - { - static struct in6_addr tmpaddr; - struct inet6_ifaddr *ifp; -@@ -766,7 +770,8 @@ void ndisc_recv_ns(struct sk_buff *skb) - int addr_type = ipv6_addr_type(saddr); - - if (in6_dev && in6_dev->cnf.forwarding && -- (addr_type & IPV6_ADDR_UNICAST) && -+ (addr_type & IPV6_ADDR_UNICAST || -+ addr_type == IPV6_ADDR_ANY) && - pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) { - int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST; - -@@ -778,13 +783,21 @@ void ndisc_recv_ns(struct sk_buff *skb) - nd_tbl.stats.rcv_probes_mcast++; - else - nd_tbl.stats.rcv_probes_ucast++; -- -- neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); - -- if (neigh) { -- ndisc_send_na(dev, neigh, saddr, &msg->target, -- 0, 1, 0, 1); -- neigh_release(neigh); -+ if (addr_type & IPV6_ADDR_UNICAST) { -+ neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); -+ -+ if (neigh) { -+ ndisc_send_na(dev, neigh, saddr, &msg->target, -+ 0, 1, 0, 1); -+ neigh_release(neigh); -+ } -+ } else { -+ /* the proxy should also protect against DAD */ -+ struct in6_addr maddr; -+ ipv6_addr_all_nodes(&maddr); -+ ndisc_send_na(dev, NULL, &maddr, &msg->target, -+ 0, 0, 0, 1); - } - } else { - struct sk_buff *n = skb_clone(skb, GFP_ATOMIC); -@@ -849,6 +862,9 @@ void ndisc_recv_na(struct sk_buff *skb) - if (ifp->flags & IFA_F_TENTATIVE) { - addrconf_dad_failure(ifp); - return; -+ } else if (ndisc_mip_mn_ha_probe(ifp, lladdr)) { -+ in6_ifa_put(ifp); -+ return; - } - /* What should we make now? The advertisement - is invalid, but ndisc specs say nothing -@@ -887,6 +903,7 @@ void ndisc_recv_na(struct sk_buff *skb) - msg->icmph.icmp6_override, 1); - neigh_release(neigh); - } -+ ndisc_check_mipv6_dad(&msg->target); - } - - static void ndisc_router_discovery(struct sk_buff *skb) -@@ -894,6 +911,7 @@ static void ndisc_router_discovery(struc - struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw; - struct neighbour *neigh; - struct inet6_dev *in6_dev; -+ int change_rtr; - struct rt6_info *rt; - int lifetime; - struct ndisc_options ndopts; -@@ -923,10 +941,6 @@ static void ndisc_router_discovery(struc - ND_PRINTK1("RA: can't find in6 device\n"); - return; - } -- if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { -- in6_dev_put(in6_dev); -- return; -- } - - if (!ndisc_parse_options(opt, optlen, &ndopts)) { - in6_dev_put(in6_dev); -@@ -935,7 +949,12 @@ static void ndisc_router_discovery(struc - "ICMP6 RA: invalid ND option, ignored.\n"); - return; - } -+ change_rtr = ndisc_mipv6_ra_rcv(skb, &ndopts); - -+ if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { -+ in6_dev_put(in6_dev); -+ return; -+ } - if (in6_dev->if_flags & IF_RS_SENT) { - /* - * flag that an RA was received after an RS was sent -@@ -963,8 +982,7 @@ static void ndisc_router_discovery(struc - ip6_del_rt(rt, NULL); - rt = NULL; - } -- -- if (rt == NULL && lifetime) { -+ if (rt == NULL && lifetime && change_rtr) { - ND_PRINTK2("ndisc_rdisc: adding default router\n"); - - rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev); -@@ -1087,6 +1105,8 @@ out: - if (rt) - dst_release(&rt->u.dst); - in6_dev_put(in6_dev); -+ -+ ndisc_mipv6_change_router(change_rtr); - } - - static void ndisc_redirect_rcv(struct sk_buff *skb) -diff -uprN linux-2.4.25.old/net/ipv6/raw.c linux-2.4.25/net/ipv6/raw.c ---- linux-2.4.25.old/net/ipv6/raw.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/raw.c 2004-06-26 11:29:32.000000000 +0100 -@@ -43,6 +43,7 @@ - #include <net/transp_v6.h> - #include <net/udp.h> - #include <net/inet_common.h> -+#include <net/mipglue.h> - - #include <net/rawv6.h> - -@@ -636,6 +637,7 @@ static int rawv6_sendmsg(struct sock *sk - hdr.daddr = daddr; - else - hdr.daddr = NULL; -+ hdr.daddr = mipv6_get_fake_hdr_daddr(hdr.daddr, daddr); - - err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len, - opt, hlimit, msg->msg_flags); -diff -uprN linux-2.4.25.old/net/ipv6/route.c linux-2.4.25/net/ipv6/route.c ---- linux-2.4.25.old/net/ipv6/route.c 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/net/ipv6/route.c 2004-06-26 11:29:32.000000000 +0100 -@@ -49,6 +49,7 @@ - #include <net/addrconf.h> - #include <net/tcp.h> - #include <linux/rtnetlink.h> -+#include <net/mipglue.h> - - #include <asm/uaccess.h> - -@@ -363,12 +364,8 @@ static struct rt6_info *rt6_cow(struct r - rt->u.dst.flags |= DST_HOST; - - #ifdef CONFIG_IPV6_SUBTREES -- if (rt->rt6i_src.plen && saddr) { -- ipv6_addr_copy(&rt->rt6i_src.addr, saddr); -- rt->rt6i_src.plen = 128; -- } -+ rt->rt6i_src.plen = ort->rt6i_src.plen; - #endif -- - rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); - - dst_hold(&rt->u.dst); -@@ -511,14 +508,19 @@ struct dst_entry * ip6_route_output(stru - struct rt6_info *rt; - int strict; - int attempts = 3; -+ struct in6_addr *saddr; - -+ if (ipv6_chk_addr(fl->nl_u.ip6_u.daddr, NULL)) -+ saddr = NULL; -+ else -+ saddr = fl->nl_u.ip6_u.saddr; -+ - strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); - - relookup: - read_lock_bh(&rt6_lock); - -- fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, -- fl->nl_u.ip6_u.saddr); -+ fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, saddr); - - restart: - rt = fn->leaf; -@@ -663,25 +665,6 @@ out: - return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size); - } - --/* Clean host part of a prefix. Not necessary in radix tree, -- but results in cleaner routing tables. -- -- Remove it only when all the things will work! -- */ -- --static void ipv6_addr_prefix(struct in6_addr *pfx, -- const struct in6_addr *addr, int plen) --{ -- int b = plen&0x7; -- int o = plen>>3; -- -- memcpy(pfx->s6_addr, addr, o); -- if (o < 16) -- memset(pfx->s6_addr + o, 0, 16 - o); -- if (b != 0) -- pfx->s6_addr[o] = addr->s6_addr[o]&(0xff00 >> b); --} -- - static int ipv6_get_mtu(struct net_device *dev) - { - int mtu = IPV6_MIN_MTU; -@@ -810,7 +793,7 @@ int ip6_route_add(struct in6_rtmsg *rtms - if (!(gwa_type&IPV6_ADDR_UNICAST)) - goto out; - -- grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); -+ grt = rt6_lookup(gw_addr, &rtmsg->rtmsg_src, rtmsg->rtmsg_ifindex, 1); - - err = -EHOSTUNREACH; - if (grt == NULL) -@@ -848,7 +831,15 @@ int ip6_route_add(struct in6_rtmsg *rtms - goto out; - } - } -- -+#ifdef USE_IPV6_MOBILITY -+ /* If destination is mobile node, add special skb->dst->input -+ * function for proxy ND. -+ */ -+ if (rtmsg->rtmsg_flags & RTF_MOBILENODE) { -+ rt->u.dst.input = ip6_mipv6_forward; -+ } -+#endif /* CONFIG_IPV6_MOBILITY */ -+ - if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) - rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; - else -@@ -936,7 +927,7 @@ void rt6_redirect(struct in6_addr *dest, - struct rt6_info *rt, *nrt; - - /* Locate old route to this destination. */ -- rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); -+ rt = rt6_lookup(dest, saddr, neigh->dev->ifindex, 1); - - if (rt == NULL) - return; -@@ -1003,6 +994,9 @@ source_ok: - nrt = ip6_rt_copy(rt); - if (nrt == NULL) - goto out; -+#ifdef CONFIG_IPV6_SUBTREES -+ nrt->rt6i_src.plen = rt->rt6i_src.plen; -+#endif - - nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; - if (on_link) -@@ -1104,6 +1098,9 @@ void rt6_pmtu_discovery(struct in6_addr - nrt = ip6_rt_copy(rt); - if (nrt == NULL) - goto out; -+#ifdef CONFIG_IPV6_SUBTREES -+ nrt->rt6i_src.plen = rt->rt6i_src.plen; -+#endif - ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); - nrt->rt6i_dst.plen = 128; - nrt->u.dst.flags |= DST_HOST; -diff -uprN linux-2.4.25.old/net/ipv6/tcp_ipv6.c linux-2.4.25/net/ipv6/tcp_ipv6.c ---- linux-2.4.25.old/net/ipv6/tcp_ipv6.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/ipv6/tcp_ipv6.c 2004-06-26 11:29:32.000000000 +0100 -@@ -50,6 +50,7 @@ - #include <net/addrconf.h> - #include <net/ip6_route.h> - #include <net/inet_ecn.h> -+#include <net/mipglue.h> - - #include <asm/uaccess.h> - -@@ -557,6 +558,7 @@ static int tcp_v6_connect(struct sock *s - struct flowi fl; - struct dst_entry *dst; - int addr_type; -+ int reroute = 0; - int err; - - if (addr_len < SIN6_LEN_RFC2133) -@@ -660,7 +662,7 @@ static int tcp_v6_connect(struct sock *s - - fl.proto = IPPROTO_TCP; - fl.fl6_dst = &np->daddr; -- fl.fl6_src = saddr; -+ fl.fl6_src = saddr; - fl.oif = sk->bound_dev_if; - fl.uli_u.ports.dport = usin->sin6_port; - fl.uli_u.ports.sport = sk->sport; -@@ -669,31 +671,46 @@ static int tcp_v6_connect(struct sock *s - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; - fl.nl_u.ip6_u.daddr = rt0->addr; - } -- - dst = ip6_route_output(sk, &fl); -- -+#ifdef CONFIG_IPV6_SUBTREES -+ reroute = (saddr == NULL); -+#endif - if ((err = dst->error) != 0) { - dst_release(dst); - goto failure; - } -- -- ip6_dst_store(sk, dst, NULL); -- sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -- -+ if (!reroute) { -+ ip6_dst_store(sk, dst, NULL, NULL); -+ sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -+ } - if (saddr == NULL) { - err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf); -+ -+ if (reroute) -+ dst_release(dst); - if (err) - goto failure; - - saddr = &saddr_buf; -+ ipv6_addr_copy(&np->rcv_saddr, saddr); -+#ifdef CONFIG_IPV6_SUBTREES -+ fl.fl6_src = saddr; -+ dst = ip6_route_output(sk, &fl); -+ -+ if ((err = dst->error) != 0) { -+ dst_release(dst); -+ goto failure; -+ } -+ ip6_dst_store(sk, dst, NULL, NULL); -+ sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -+#endif - } - - /* set the source address */ -- ipv6_addr_copy(&np->rcv_saddr, saddr); - ipv6_addr_copy(&np->saddr, saddr); - sk->rcv_saddr= LOOPBACK4_IPV6; - -- tp->ext_header_len = 0; -+ tp->ext_header_len = tcp_v6_get_mipv6_header_len(); - if (np->opt) - tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen; - tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); -@@ -1338,7 +1355,7 @@ static struct sock * tcp_v6_syn_recv_soc - #endif - MOD_INC_USE_COUNT; - -- ip6_dst_store(newsk, dst, NULL); -+ ip6_dst_store(newsk, dst, NULL, NULL); - sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - - newtp = &(newsk->tp_pinfo.af_tcp); -@@ -1383,7 +1400,7 @@ static struct sock * tcp_v6_syn_recv_soc - sock_kfree_s(sk, opt, opt->tot_len); - } - -- newtp->ext_header_len = 0; -+ newtp->ext_header_len = tcp_v6_get_mipv6_header_len(); - if (np->opt) - newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen; - -@@ -1710,7 +1727,7 @@ static int tcp_v6_rebuild_header(struct - return err; - } - -- ip6_dst_store(sk, dst, NULL); -+ ip6_dst_store(sk, dst, NULL, NULL); - sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - } - -@@ -1749,7 +1766,7 @@ static int tcp_v6_xmit(struct sk_buff *s - return -sk->err_soft; - } - -- ip6_dst_store(sk, dst, NULL); -+ ip6_dst_store(sk, dst, NULL, NULL); - } - - skb->dst = dst_clone(dst); -diff -uprN linux-2.4.25.old/net/ipv6/udp.c linux-2.4.25/net/ipv6/udp.c ---- linux-2.4.25.old/net/ipv6/udp.c 2004-02-18 13:36:32.000000000 +0000 -+++ linux-2.4.25/net/ipv6/udp.c 2004-06-26 11:29:32.000000000 +0100 -@@ -48,6 +48,7 @@ - #include <net/ip.h> - #include <net/udp.h> - #include <net/inet_common.h> -+#include <net/mipglue.h> - - #include <net/checksum.h> - -@@ -232,6 +233,7 @@ int udpv6_connect(struct sock *sk, struc - struct ip6_flowlabel *flowlabel = NULL; - int addr_type; - int err; -+ int reroute = 0; - - if (usin->sin6_family == AF_INET) { - if (__ipv6_only_sock(sk)) -@@ -331,7 +333,7 @@ ipv4_connected: - - fl.proto = IPPROTO_UDP; - fl.fl6_dst = &np->daddr; -- fl.fl6_src = &saddr; -+ fl.fl6_src = NULL; - fl.oif = sk->bound_dev_if; - fl.uli_u.ports.dport = sk->dport; - fl.uli_u.ports.sport = sk->sport; -@@ -348,29 +350,44 @@ ipv4_connected: - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; - fl.fl6_dst = rt0->addr; - } -- - dst = ip6_route_output(sk, &fl); -- - if ((err = dst->error) != 0) { - dst_release(dst); - fl6_sock_release(flowlabel); -- return err; -- } -- -- ip6_dst_store(sk, dst, fl.fl6_dst); -- -+ return err; -+ } -+#ifdef CONFIG_IPV6_SUBTREES -+ reroute = (fl.fl6_src == NULL); -+#endif - /* get the source adddress used in the apropriate device */ - - err = ipv6_get_saddr(dst, daddr, &saddr); - -+ if (reroute) -+ dst_release(dst); -+ - if (err == 0) { -- if(ipv6_addr_any(&np->saddr)) -+#ifdef CONFIG_IPV6_SUBTREES -+ if (reroute) { -+ fl.fl6_src = &saddr; -+ dst = ip6_route_output(sk, &fl); -+ if ((err = dst->error) != 0) { -+ dst_release(dst); -+ fl6_sock_release(flowlabel); -+ return err; -+ } -+ } -+#endif -+ if(ipv6_addr_any(&np->saddr)) { - ipv6_addr_copy(&np->saddr, &saddr); -- -+ fl.fl6_src = &np->saddr; -+ } - if(ipv6_addr_any(&np->rcv_saddr)) { - ipv6_addr_copy(&np->rcv_saddr, &saddr); - sk->rcv_saddr = LOOPBACK4_IPV6; - } -+ ip6_dst_store(sk, dst, fl.fl6_dst, -+ fl.fl6_src == &np->saddr ? fl.fl6_src : NULL); - sk->state = TCP_ESTABLISHED; - } - fl6_sock_release(flowlabel); -@@ -894,6 +911,7 @@ static int udpv6_sendmsg(struct sock *sk - opt = fl6_merge_options(&opt_space, flowlabel, opt); - if (opt && opt->srcrt) - udh.daddr = daddr; -+ udh.daddr = mipv6_get_fake_hdr_daddr(udh.daddr, daddr); - - udh.uh.source = sk->sport; - udh.uh.len = len < 0x10000 ? htons(len) : 0; -diff -uprN linux-2.4.25.old/net/netsyms.c linux-2.4.25/net/netsyms.c ---- linux-2.4.25.old/net/netsyms.c 2003-11-28 18:26:21.000000000 +0000 -+++ linux-2.4.25/net/netsyms.c 2004-06-26 11:29:32.000000000 +0100 -@@ -190,6 +190,7 @@ EXPORT_SYMBOL(neigh_sysctl_register); - #endif - EXPORT_SYMBOL(pneigh_lookup); - EXPORT_SYMBOL(pneigh_enqueue); -+EXPORT_SYMBOL(pneigh_delete); - EXPORT_SYMBOL(neigh_destroy); - EXPORT_SYMBOL(neigh_parms_alloc); - EXPORT_SYMBOL(neigh_parms_release); diff --git a/packages/linux/files/mipv6-1.1-v2.4.27.patch b/packages/linux/files/mipv6-1.1-v2.4.27.patch deleted file mode 100644 index b8fb071d28..0000000000 --- a/packages/linux/files/mipv6-1.1-v2.4.27.patch +++ /dev/null @@ -1,19736 +0,0 @@ - -# -# Patch managed by http://www.holgerschurig.de/patcher.html -# - ---- linux-2.4.27/Documentation/Configure.help~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/Documentation/Configure.help -@@ -6308,6 +6308,57 @@ - - It is safe to say N here for now. - -+IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL) -+CONFIG_IPV6_TUNNEL -+ Experimental IP6-IP6 tunneling. You must select this, if you want -+ to use CONFIG_IPV6_MOBILITY. More information in MIPL Mobile IPv6 -+ instructions. -+ -+ If you don't want IP6-IP6 tunnels and Mobile IPv6, say N. -+ -+IPv6: Mobility Support (EXPERIMENTAL) -+CONFIG_IPV6_MOBILITY -+ This is experimental support for the upcoming specification of -+ Mobile IPv6. Mobile IPv6 allows nodes to seamlessly move between -+ networks without changing their IP addresses, thus allowing them to -+ maintain upper layer connections (e.g. TCP). Selecting this option -+ allows your computer to act as a Correspondent Node (CN). A MIPv6 -+ Mobile Node will be able to communicate with the CN and use route -+ optimization. -+ -+ For more information and configuration details, see -+ http://www.mipl.mediapoli.com/. -+ -+ If unsure, say N. -+ -+MIPv6: Mobile Node Support -+CONFIG_IPV6_MOBILITY_MN -+ If you want your computer to be a MIPv6 Mobile Node (MN), select -+ this option. You must configure MN using the userspace tools -+ available at http://www.mipl.mediapoli.com/download/mipv6-tools/. -+ -+ If your computer is stationary, or you are unsure if you need this, -+ say N. Note that you will need a properly configured MIPv6 Home -+ Agent to use any Mobile Nodes. -+ -+MIPv6: Home Agent Support -+CONFIG_IPV6_MOBILITY_HA -+ If you want your router to serve as a MIPv6 Home Agent (HA), select -+ this option. You must configure HA using the userspace tools -+ available at http://www.mipl.mediapoli.com/download/mipv6-tools/. -+ -+ If your computer is not a router, or you are unsure if you need -+ this, say N. -+ -+MIPv6: Debug messages -+CONFIG_IPV6_MOBILITY_DEBUG -+ MIPL Mobile IPv6 can produce a lot of debugging messages. There are -+ eight debug levels (0 through 7) and the level is controlled via -+ /proc/sys/net/ipv6/mobility/debuglevel. Since MIPL is still -+ experimental, you might want to say Y here. -+ -+ Be sure to say Y and record debug messages when submitting a bug -+ report. - The SCTP Protocol (EXPERIMENTAL) - CONFIG_IP_SCTP - Stream Control Transmission Protocol ---- linux-2.4.27/Documentation/DocBook/Makefile~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/Documentation/DocBook/Makefile -@@ -2,7 +2,7 @@ - kernel-api.sgml parportbook.sgml kernel-hacking.sgml \ - kernel-locking.sgml via-audio.sgml mousedrivers.sgml sis900.sgml \ - deviceiobook.sgml procfs-guide.sgml tulip-user.sgml \ -- journal-api.sgml libata.sgml -+ journal-api.sgml libata.sgml mip6-func.sgml - - PS := $(patsubst %.sgml, %.ps, $(BOOKS)) - PDF := $(patsubst %.sgml, %.pdf, $(BOOKS)) -@@ -96,6 +96,9 @@ - procfs-guide.sgml: procfs-guide.tmpl procfs_example.sgml - $(TOPDIR)/scripts/docgen < procfs-guide.tmpl >$@ - -+mip6-func.sgml: mip6-func.tmpl -+ $(TOPDIR)/scripts/docgen <$< >$@ -+ - APISOURCES := $(TOPDIR)/drivers/media/video/videodev.c \ - $(TOPDIR)/arch/i386/kernel/irq.c \ - $(TOPDIR)/arch/i386/kernel/mca.c \ ---- /dev/null -+++ linux-2.4.27/Documentation/DocBook/mip6-func.tmpl -@@ -0,0 +1,756 @@ -+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook V3.1//EN"[]> -+<book id="LinuxMobileIPv6"> -+ <bookinfo> -+ <title>MIPL Mobile IPv6 Function Reference Guide</title> -+ -+ <authorgroup> -+ <author> -+ <othername>MIPL Mobile IPv6 for Linux Team</othername> -+ <affiliation> -+ <orgname>Helsinki University of Technology</orgname> -+ <orgdiv>Telecommunications Software and Multimedia Lab</orgdiv> -+ <address> -+ <pob>PO BOX 9201</pob> -+ <postcode>FIN-02015 HUT</postcode> -+ <country>Finland</country> -+ <email>mipl@list.mipl.mediapoli.com</email> -+ </address> -+ </affiliation> -+ </author> -+ </authorgroup> -+ -+ <copyright> -+ <year>2000-2001</year> -+ <holder>Helsinki University of Technology</holder> -+ </copyright> -+ -+ <legalnotice> -+ <para> -+ Copyright (c) 2001, 2002 MIPL Mobile IPv6 for Linux Team. -+ </para> -+ <para> -+ Permission is granted to copy, distribute and/or modify this -+ document under the terms of the GNU Free Documentation License, -+ Version 1.1 published by the Free Software Foundation; with the -+ Invariant Sections being "Introduction", with the Front-Cover -+ Texts being "MIPL Mobile IPv6 Function Reference Guide", "MIPL -+ Mobile IPv6 for Linux Team" and "Helsinki University of -+ Technology". A copy of the license is included in <xref -+ linkend="gfdl">. -+ </para> -+ -+ </legalnotice> -+ </bookinfo> -+ -+<toc></toc> -+ -+ <preface id="intro"> -+ <title>Introduction</title> -+ -+ <para> -+ MIPL Mobile IPv6 for Linux is an implementation of Mobility -+ Support in IPv6 IETF mobile-ip working groups Internet-Draft -+ (draft-ietf-mobileip-ipv6). This implementation has been -+ developed in the Telecommunications Software and Multimedia -+ Laboratory at Helsinki University of Technology. -+ </para> -+ -+ <para> -+ MIPL is fully open source, licensed under the GNU General -+ Public License. Latest source for MIPL can be downloaded from -+ the MIPL website at: -+ </para> -+ <programlisting> -+ http://www.mipl.mediapoli.com/. -+ </programlisting> -+ <para> -+ Developers and users interested in MIPL can subscribe to the -+ MIPL mailing list by sending e-mail to -+ <email>majordomo@list.mipl.mediapoli.com</email> with -+ </para> -+ <programlisting> -+ subscribe mipl -+ </programlisting> -+ <para> -+ in the body of the message. -+ </para> -+ -+ <para> -+ This document is a reference guide to MIPL functions. Intended -+ audience is developers wishing to contribute to the project. -+ Hopefully this document will make it easier and quicker to -+ understand and adopt the inner workings of MIPL Mobile IPv6. -+ </para> -+ -+ <para> -+ MIPL Mobile IPv6 for Linux Team members (past and present): -+ -+ <itemizedlist> -+ <listitem> -+ <address> -+ Sami Kivisaari <email>Sami.Kivisaari@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Niklas Kampe <email>Niklas.Kampe@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Juha Mynttinen <email>Juha.Mynttinen@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Toni Nykanen <email>Toni.Nykanen@iki.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Henrik Petander <email>Henrik.Petander@hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Antti Tuominen <email>ajtuomin@tml.hut.fi</email> -+ </address> -+ </listitem> -+ </itemizedlist> -+ -+ <itemizedlist> -+ <listitem> -+ <address> -+ Marko Myllynen -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Ville Nuorvala <email>vnuorval@tcs.hut.fi</email> -+ </address> -+ </listitem> -+ <listitem> -+ <address> -+ Jaakko Laine <email>Jaakko.Laine@hut.fi</email> -+ </address> -+ </listitem> -+ </itemizedlist> -+ </para> -+ -+ </preface> -+ -+ <chapter id="common"> -+ <title>Common functions for all entities</title> -+ -+ <sect1><title>Low-level functions</title> -+ <para> -+ These functions implement memory allocation used by others. -+ Hashlist functions implement a linked list with hash lookup, -+ which is used with Binding Update List, Binding Cache, Home -+ Agents List etc. -+ </para> -+!Inet/ipv6/mobile_ip6/mempool.h -+!Inet/ipv6/mobile_ip6/hashlist.h -+ </sect1> -+ -+ <sect1><title>Debug functions</title> -+ <para> -+ Debug and utility functions. These functions are available if -+ <constant>CONFIG_IPV6_MOBILITY_DEBUG</constant> is set. -+ Otherwise macros expand to no operation. -+ </para> -+!Inet/ipv6/mobile_ip6/debug.h -+!Inet/ipv6/mobile_ip6/mipv6.c -+ </sect1> -+ -+ <sect1><title>Extension Header functions</title> -+ <para> -+ These functions create and handle extension headers that are -+ specific to MIPv6. -+ </para> -+!Inet/ipv6/mobile_ip6/exthdrs.c -+ </sect1> -+ -+ <sect1><title>Mobility Header functions</title> -+ <para> -+ MIPv6 specifies a new protocol called Mobility Header. -+ Mobility Header has several message types. Messages may also -+ carry Mobility Options. These functions are used to create and -+ handle Mobility Headers and Mobility Options. -+ </para> -+!Inet/ipv6/mobile_ip6/sendopts.c -+!Inet/ipv6/mobile_ip6/mh_recv.c -+!Inet/ipv6/mobile_ip6/auth_subopt.c -+ </sect1> -+ -+ <sect1><title>Binding Cache</title> -+ <para> -+ All Mobile IPv6 entities have a binding cache. These functions -+ provide easy manipulation of the binding cache. -+ </para> -+!Inet/ipv6/mobile_ip6/bcache.c -+ </sect1> -+ -+ <sect1><title>Security</title> -+ -+ <para> -+ These functions are common authentication functions and -+ implement Draft 13 style IPSec AH support for Binding Updates. -+ </para> -+!Inet/ipv6/mobile_ip6/ah_algo.c -+!Inet/ipv6/mobile_ip6/sadb.c -+!Inet/ipv6/mobile_ip6/ah.c -+ </sect1> -+ -+ <sect1><title>Utility functions</title> -+ -+ <para> -+ These functions are general utility functions commonly used by -+ all entities. -+ </para> -+!Inet/ipv6/mobile_ip6/util.c -+ </sect1> -+ -+ </chapter> -+ -+ <chapter id="mn"> -+ <title>Mobile Node functions</title> -+ <sect1><title>General functions</title> -+ <para> -+ </para> -+!Inet/ipv6/mobile_ip6/mn.c -+ </sect1> -+ -+ <sect1><title>Binding Update List</title> -+ <para> -+ Mobile Node keeps track of sent binding updates in Binding -+ Update List. -+ </para> -+!Inet/ipv6/mobile_ip6/bul.c -+ </sect1> -+ -+ <sect1><title>Movement detection</title> -+ -+ <para> -+ These functions are used by the mobile node for movement -+ detection. -+ </para> -+!Inet/ipv6/mobile_ip6/mdetect.c -+ </sect1> -+ </chapter> -+ -+ <chapter id="ha"> -+ <title>Home Agent functions</title> -+ <sect1><title>General functions</title> -+ <para> -+ </para> -+!Inet/ipv6/mobile_ip6/ha.c -+ </sect1> -+ -+ <sect1><title>Duplicate Address Detection functions</title> -+ <para> -+ Home Agent does Duplicate Address Detection for Mobile Nodes' -+ addresses. These functions implement MIPv6 specific DAD -+ functionality. -+ </para> -+!Inet/ipv6/mobile_ip6/dad.c -+ </sect1> -+ -+ </chapter> -+ <appendix id="gfdl"> -+ <title>GNU Free Documentation License</title> -+ -+ <para> -+ Version 1.1, March 2000 -+ </para> -+ -+ <programlisting> -+ Copyright (C) 2000 Free Software Foundation, Inc. -+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -+ Everyone is permitted to copy and distribute verbatim copies -+ of this license document, but changing it is not allowed. -+ </programlisting> -+ -+ <sect1><title>0. PREAMBLE</title> -+ -+ <para> -+ The purpose of this License is to make a manual, textbook, or -+ other written document "free" in the sense of freedom: to -+ assure everyone the effective freedom to copy and redistribute -+ it, with or without modifying it, either commercially or -+ noncommercially. Secondarily, this License preserves for the -+ author and publisher a way to get credit for their work, while -+ not being considered responsible for modifications made by -+ others. -+ </para> -+ -+ <para> -+ This License is a kind of "copyleft", which means that -+ derivative works of the document must themselves be free in the -+ same sense. It complements the GNU General Public License, -+ which is a copyleft license designed for free software. -+ </para> -+ -+ <para> -+ We have designed this License in order to use it for manuals -+ for free software, because free software needs free -+ documentation: a free program should come with manuals -+ providing the same freedoms that the software does. But this -+ License is not limited to software manuals; it can be used for -+ any textual work, regardless of subject matter or whether it is -+ published as a printed book. We recommend this License -+ principally for works whose purpose is instruction or -+ reference. -+ </para> -+ -+ </sect1> -+ <sect1><title>1. APPLICABILITY AND DEFINITIONS</title> -+ -+ <para> -+ This License applies to any manual or other work that contains -+ a notice placed by the copyright holder saying it can be -+ distributed under the terms of this License. The "Document", -+ below, refers to any such manual or work. Any member of the -+ public is a licensee, and is addressed as "you". -+ </para> -+ -+ <para> -+ A "Modified Version" of the Document means any work containing -+ the Document or a portion of it, either copied verbatim, or -+ with modifications and/or translated into another language. -+ </para> -+ -+ <para> -+ A "Secondary Section" is a named appendix or a front-matter -+ section of the Document that deals exclusively with the -+ relationship of the publishers or authors of the Document to -+ the Document's overall subject (or to related matters) and -+ contains nothing that could fall directly within that overall -+ subject. (For example, if the Document is in part a textbook of -+ mathematics, a Secondary Section may not explain any -+ mathematics.) The relationship could be a matter of historical -+ connection with the subject or with related matters, or of -+ legal, commercial, philosophical, ethical or political position -+ regarding them. -+ </para> -+ -+ <para> -+ The "Invariant Sections" are certain Secondary Sections whose -+ titles are designated, as being those of Invariant Sections, in -+ the notice that says that the Document is released under this -+ License. -+ </para> -+ -+ <para> -+ The "Cover Texts" are certain short passages of text that are -+ listed, as Front-Cover Texts or Back-Cover Texts, in the notice -+ that says that the Document is released under this License. -+ </para> -+ -+ <para> -+ A "Transparent" copy of the Document means a machine-readable -+ copy, represented in a format whose specification is available -+ to the general public, whose contents can be viewed and edited -+ directly and straightforwardly with generic text editors or -+ (for images composed of pixels) generic paint programs or (for -+ drawings) some widely available drawing editor, and that is -+ suitable for input to text formatters or for automatic -+ translation to a variety of formats suitable for input to text -+ formatters. A copy made in an otherwise Transparent file format -+ whose markup has been designed to thwart or discourage -+ subsequent modification by readers is not Transparent. A copy -+ that is not "Transparent" is called "Opaque". -+ </para> -+ -+ <para> -+ Examples of suitable formats for Transparent copies include -+ plain ASCII without markup, Texinfo input format, LaTeX input -+ format, SGML or XML using a publicly available DTD, and -+ standard-conforming simple HTML designed for human -+ modification. Opaque formats include PostScript, PDF, -+ proprietary formats that can be read and edited only by -+ proprietary word processors, SGML or XML for which the DTD -+ and/or processing tools are not generally available, and the -+ machine-generated HTML produced by some word processors for -+ output purposes only. -+ </para> -+ -+ <para> -+ The "Title Page" means, for a printed book, the title page -+ itself, plus such following pages as are needed to hold, -+ legibly, the material this License requires to appear in the -+ title page. For works in formats which do not have any title -+ page as such, "Title Page" means the text near the most -+ prominent appearance of the work's title, preceding the -+ beginning of the body of the text. -+ </para> -+ -+ </sect1> -+ <sect1><title>2. VERBATIM COPYING</title> -+ -+ <para> -+ You may copy and distribute the Document in any medium, either -+ commercially or noncommercially, provided that this License, -+ the copyright notices, and the license notice saying this -+ License applies to the Document are reproduced in all copies, -+ and that you add no other conditions whatsoever to those of -+ this License. You may not use technical measures to obstruct or -+ control the reading or further copying of the copies you make -+ or distribute. However, you may accept compensation in exchange -+ for copies. If you distribute a large enough number of copies -+ you must also follow the conditions in section 3. -+ </para> -+ -+ <para> -+ You may also lend copies, under the same conditions stated -+ above, and you may publicly display copies. -+ </para> -+ -+ </sect1> -+ <sect1><title>3. COPYING IN QUANTITY</title> -+ -+ <para> -+ If you publish printed copies of the Document numbering more -+ than 100, and the Document's license notice requires Cover -+ Texts, you must enclose the copies in covers that carry, -+ clearly and legibly, all these Cover Texts: Front-Cover Texts -+ on the front cover, and Back-Cover Texts on the back -+ cover. Both covers must also clearly and legibly identify you -+ as the publisher of these copies. The front cover must present -+ the full title with all words of the title equally prominent -+ and visible. You may add other material on the covers in -+ addition. Copying with changes limited to the covers, as long -+ as they preserve the title of the Document and satisfy these -+ conditions, can be treated as verbatim copying in other -+ respects. -+ </para> -+ -+ <para> -+ If the required texts for either cover are too voluminous to -+ fit legibly, you should put the first ones listed (as many as -+ fit reasonably) on the actual cover, and continue the rest onto -+ adjacent pages. -+ </para> -+ -+ <para> -+ If you publish or distribute Opaque copies of the Document -+ numbering more than 100, you must either include a -+ machine-readable Transparent copy along with each Opaque copy, -+ or state in or with each Opaque copy a publicly-accessible -+ computer-network location containing a complete Transparent -+ copy of the Document, free of added material, which the general -+ network-using public has access to download anonymously at no -+ charge using public-standard network protocols. If you use the -+ latter option, you must take reasonably prudent steps, when you -+ begin distribution of Opaque copies in quantity, to ensure that -+ this Transparent copy will remain thus accessible at the stated -+ location until at least one year after the last time you -+ distribute an Opaque copy (directly or through your agents or -+ retailers) of that edition to the public. -+ </para> -+ -+ <para> -+ It is requested, but not required, that you contact the authors -+ of the Document well before redistributing any large number of -+ copies, to give them a chance to provide you with an updated -+ version of the Document. -+ </para> -+ -+ </sect1> -+ <sect1><title>4. MODIFICATIONS</title> -+ -+ <para> -+ You may copy and distribute a Modified Version of the Document -+ under the conditions of sections 2 and 3 above, provided that -+ you release the Modified Version under precisely this License, -+ with the Modified Version filling the role of the Document, -+ thus licensing distribution and modification of the Modified -+ Version to whoever possesses a copy of it. In addition, you -+ must do these things in the Modified Version: -+ </para> -+ -+ <para> -+ <itemizedlist spacing=compact> -+ <listitem> -+ <para> -+ A. Use in the Title Page (and on the covers, if any) a title -+ distinct from that of the Document, and from those of previous -+ versions (which should, if there were any, be listed in the -+ History section of the Document). You may use the same title -+ as a previous version if the original publisher of that -+ version gives permission. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ B. List on the Title Page, as authors, one or more persons -+ or entities responsible for authorship of the modifications in -+ the Modified Version, together with at least five of the -+ principal authors of the Document (all of its principal -+ authors, if it has less than five). -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ C. State on the Title page the name of the publisher of the -+ Modified Version, as the publisher. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ D. Preserve all the copyright notices of the Document. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ E. Add an appropriate copyright notice for your -+ modifications adjacent to the other copyright notices. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ F. Include, immediately after the copyright notices, a -+ license notice giving the public permission to use the -+ Modified Version under the terms of this License, in the form -+ shown in the Addendum below. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ G. Preserve in that license notice the full lists of -+ Invariant Sections and required Cover Texts given in the -+ Document's license notice. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ H. Include an unaltered copy of this License. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ I. Preserve the section entitled "History", and its title, -+ and add to it an item stating at least the title, year, new -+ authors, and publisher of the Modified Version as given on the -+ Title Page. If there is no section entitled "History" in the -+ Document, create one stating the title, year, authors, and -+ publisher of the Document as given on its Title Page, then add -+ an item describing the Modified Version as stated in the -+ previous sentence. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ J. Preserve the network location, if any, given in the -+ Document for public access to a Transparent copy of the -+ Document, and likewise the network locations given in the -+ Document for previous versions it was based on. These may be -+ placed in the "History" section. You may omit a network -+ location for a work that was published at least four years -+ before the Document itself, or if the original publisher of -+ the version it refers to gives permission. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ K. In any section entitled "Acknowledgements" or -+ "Dedications", preserve the section's title, and preserve in -+ the section all the substance and tone of each of the -+ contributor acknowledgements and/or dedications given therein. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ L. Preserve all the Invariant Sections of the Document, -+ unaltered in their text and in their titles. Section numbers -+ or the equivalent are not considered part of the section -+ titles. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ M. Delete any section entitled "Endorsements". Such a -+ section may not be included in the Modified Version. -+ </para> -+ </listitem> -+ <listitem> -+ <para> -+ N. Do not retitle any existing section as "Endorsements" or -+ to conflict in title with any Invariant Section. -+ </para> -+ </listitem> -+ </itemizedlist> -+ </para> -+ -+ <para> -+ If the Modified Version includes new front-matter sections or -+ appendices that qualify as Secondary Sections and contain no -+ material copied from the Document, you may at your option -+ designate some or all of these sections as invariant. To do -+ this, add their titles to the list of Invariant Sections in the -+ Modified Version's license notice. These titles must be -+ distinct from any other section titles. -+ </para> -+ -+ <para> -+ You may add a section entitled "Endorsements", provided it -+ contains nothing but endorsements of your Modified Version by -+ various parties--for example, statements of peer review or that -+ the text has been approved by an organization as the -+ authoritative definition of a standard. -+ </para> -+ -+ <para> -+ You may add a passage of up to five words as a Front-Cover -+ Text, and a passage of up to 25 words as a Back-Cover Text, to -+ the end of the list of Cover Texts in the Modified -+ Version. Only one passage of Front-Cover Text and one of -+ Back-Cover Text may be added by (or through arrangements made -+ by) any one entity. If the Document already includes a cover -+ text for the same cover, previously added by you or by -+ arrangement made by the same entity you are acting on behalf -+ of, you may not add another; but you may replace the old one, -+ on explicit permission from the previous publisher that added -+ the old one. -+ </para> -+ -+ <para> -+ The author(s) and publisher(s) of the Document do not by this -+ License give permission to use their names for publicity for or -+ to assert or imply endorsement of any Modified Version. -+ </para> -+ -+ </sect1> -+ <sect1><title>5. COMBINING DOCUMENTS</title> -+ -+ <para> -+ You may combine the Document with other documents released -+ under this License, under the terms defined in section 4 above -+ for modified versions, provided that you include in the -+ combination all of the Invariant Sections of all of the -+ original documents, unmodified, and list them all as Invariant -+ Sections of your combined work in its license notice. -+ </para> -+ -+ <para> -+ The combined work need only contain one copy of this License, -+ and multiple identical Invariant Sections may be replaced with -+ a single copy. If there are multiple Invariant Sections with -+ the same name but different contents, make the title of each -+ such section unique by adding at the end of it, in parentheses, -+ the name of the original author or publisher of that section if -+ known, or else a unique number. Make the same adjustment to the -+ section titles in the list of Invariant Sections in the license -+ notice of the combined work. -+ </para> -+ -+ <para> -+ In the combination, you must combine any sections entitled -+ "History" in the various original documents, forming one -+ section entitled "History"; likewise combine any sections -+ entitled "Acknowledgements", and any sections entitled -+ "Dedications". You must delete all sections entitled -+ "Endorsements." -+ </para> -+ -+ </sect1> -+ <sect1><title>6. COLLECTIONS OF DOCUMENTS</title> -+ -+ <para> -+ You may make a collection consisting of the Document and other -+ documents released under this License, and replace the -+ individual copies of this License in the various documents with -+ a single copy that is included in the collection, provided that -+ you follow the rules of this License for verbatim copying of -+ each of the documents in all other respects. -+ </para> -+ -+ <para> -+ You may extract a single document from such a collection, and -+ distribute it individually under this License, provided you -+ insert a copy of this License into the extracted document, and -+ follow this License in all other respects regarding verbatim -+ copying of that document. -+ </para> -+ -+ </sect1> -+ <sect1><title>7. AGGREGATION WITH INDEPENDENT WORKS</title> -+ -+ <para> -+ A compilation of the Document or its derivatives with other -+ separate and independent documents or works, in or on a volume -+ of a storage or distribution medium, does not as a whole count -+ as a Modified Version of the Document, provided no compilation -+ copyright is claimed for the compilation. Such a compilation is -+ called an "aggregate", and this License does not apply to the -+ other self-contained works thus compiled with the Document, on -+ account of their being thus compiled, if they are not -+ themselves derivative works of the Document. -+ </para> -+ -+ <para> -+ If the Cover Text requirement of section 3 is applicable to -+ these copies of the Document, then if the Document is less than -+ one quarter of the entire aggregate, the Document's Cover Texts -+ may be placed on covers that surround only the Document within -+ the aggregate. Otherwise they must appear on covers around the -+ whole aggregate. -+ </para> -+ -+ </sect1> -+ <sect1><title>8. TRANSLATION</title> -+ -+ <para> -+ Translation is considered a kind of modification, so you may -+ distribute translations of the Document under the terms of -+ section 4. Replacing Invariant Sections with translations -+ requires special permission from their copyright holders, but -+ you may include translations of some or all Invariant Sections -+ in addition to the original versions of these Invariant -+ Sections. You may include a translation of this License -+ provided that you also include the original English version of -+ this License. In case of a disagreement between the translation -+ and the original English version of this License, the original -+ English version will prevail. -+ </para> -+ -+ </sect1> -+ <sect1><title>9. TERMINATION</title> -+ -+ <para> -+ You may not copy, modify, sublicense, or distribute the -+ Document except as expressly provided for under this -+ License. Any other attempt to copy, modify, sublicense or -+ distribute the Document is void, and will automatically -+ terminate your rights under this License. However, parties who -+ have received copies, or rights, from you under this License -+ will not have their licenses terminated so long as such parties -+ remain in full compliance. -+ </para> -+ -+ </sect1> -+ <sect1><title>10. FUTURE REVISIONS OF THIS LICENSE</title> -+ -+ <para> -+ The Free Software Foundation may publish new, revised versions -+ of the GNU Free Documentation License from time to time. Such -+ new versions will be similar in spirit to the present version, -+ but may differ in detail to address new problems or -+ concerns. See http://www.gnu.org/copyleft/. -+ </para> -+ -+ <para> -+ Each version of the License is given a distinguishing version -+ number. If the Document specifies that a particular numbered -+ version of this License "or any later version" applies to it, -+ you have the option of following the terms and conditions -+ either of that specified version or of any later version that -+ has been published (not as a draft) by the Free Software -+ Foundation. If the Document does not specify a version number -+ of this License, you may choose any version ever published (not -+ as a draft) by the Free Software Foundation. -+ </para> -+ -+ </sect1> -+ </appendix> -+</book> ---- linux-2.4.27/include/linux/icmpv6.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/icmpv6.h -@@ -40,14 +40,16 @@ - struct icmpv6_nd_ra { - __u8 hop_limit; - #if defined(__LITTLE_ENDIAN_BITFIELD) -- __u8 reserved:6, -+ __u8 reserved:5, -+ home_agent:1, - other:1, - managed:1; - - #elif defined(__BIG_ENDIAN_BITFIELD) - __u8 managed:1, - other:1, -- reserved:6; -+ home_agent:1, -+ reserved:5; - #else - #error "Please fix <asm/byteorder.h>" - #endif -@@ -70,6 +72,7 @@ - #define icmp6_addrconf_managed icmp6_dataun.u_nd_ra.managed - #define icmp6_addrconf_other icmp6_dataun.u_nd_ra.other - #define icmp6_rt_lifetime icmp6_dataun.u_nd_ra.rt_lifetime -+#define icmp6_home_agent icmp6_dataun.u_nd_ra.home_agent - }; - - ---- linux-2.4.27/include/linux/if_arp.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/if_arp.h -@@ -59,7 +59,7 @@ - #define ARPHRD_RAWHDLC 518 /* Raw HDLC */ - - #define ARPHRD_TUNNEL 768 /* IPIP tunnel */ --#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */ -+#define ARPHRD_TUNNEL6 769 /* IP6IP6 tunnel */ - #define ARPHRD_FRAD 770 /* Frame Relay Access Device */ - #define ARPHRD_SKIP 771 /* SKIP vif */ - #define ARPHRD_LOOPBACK 772 /* Loopback device */ ---- linux-2.4.27/include/linux/in6.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/in6.h -@@ -142,6 +142,11 @@ - #define IPV6_TLV_JUMBO 194 - - /* -+ * Mobile IPv6 TLV options. -+ */ -+#define MIPV6_TLV_HOMEADDR 201 -+ -+/* - * IPV6 socket options - */ - ---- linux-2.4.27/include/linux/ipv6.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/ipv6.h -@@ -29,6 +29,7 @@ - - #define IPV6_SRCRT_STRICT 0x01 /* this hop must be a neighbor */ - #define IPV6_SRCRT_TYPE_0 0 /* IPv6 type 0 Routing Header */ -+#define IPV6_SRCRT_TYPE_2 2 /* type 2 for Mobile IPv6 */ - - /* - * routing header -@@ -71,6 +72,19 @@ - struct in6_addr addr[0]; - - #define rt0_type rt_hdr.type -+ -+}; -+ -+/* -+ * routing header type 2 -+ */ -+ -+struct rt2_hdr { -+ struct ipv6_rt_hdr rt_hdr; -+ __u32 reserved; -+ struct in6_addr addr; -+ -+#define rt2_type rt_hdr.type; - }; - - /* -@@ -156,12 +170,16 @@ - struct inet6_skb_parm - { - int iif; -+ __u8 mipv6_flags; - __u16 ra; - __u16 hop; - __u16 auth; - __u16 dst0; - __u16 srcrt; -+ __u16 srcrt2; -+ __u16 hao; - __u16 dst1; -+ struct in6_addr hoa; - }; - - #endif ---- linux-2.4.27/include/linux/ipv6_route.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/ipv6_route.h -@@ -33,6 +33,7 @@ - #define RTF_CACHE 0x01000000 /* cache entry */ - #define RTF_FLOW 0x02000000 /* flow significant route */ - #define RTF_POLICY 0x04000000 /* policy route */ -+#define RTF_MOBILENODE 0x10000000 /* for routing to Mobile Node */ - - #define RTF_LOCAL 0x80000000 - ---- /dev/null -+++ linux-2.4.27/include/linux/ipv6_tunnel.h -@@ -0,0 +1,34 @@ -+/* -+ * $Id$ -+ */ -+ -+#ifndef _IPV6_TUNNEL_H -+#define _IPV6_TUNNEL_H -+ -+#define IPV6_TLV_TNL_ENCAP_LIMIT 4 -+#define IPV6_DEFAULT_TNL_ENCAP_LIMIT 4 -+ -+/* don't add encapsulation limit if one isn't present in inner packet */ -+#define IP6_TNL_F_IGN_ENCAP_LIMIT 0x1 -+/* copy the traffic class field from the inner packet */ -+#define IP6_TNL_F_USE_ORIG_TCLASS 0x2 -+/* copy the flowlabel from the inner packet */ -+#define IP6_TNL_F_USE_ORIG_FLOWLABEL 0x4 -+/* created and maintained from within the kernel */ -+#define IP6_TNL_F_KERNEL_DEV 0x8 -+/* being used for Mobile IPv6 */ -+#define IP6_TNL_F_MIP6_DEV 0x10 -+ -+struct ip6_tnl_parm { -+ char name[IFNAMSIZ]; /* name of tunnel device */ -+ int link; /* ifindex of underlying L2 interface */ -+ __u8 proto; /* tunnel protocol */ -+ __u8 encap_limit; /* encapsulation limit for tunnel */ -+ __u8 hop_limit; /* hop limit for tunnel */ -+ __u32 flowinfo; /* traffic class and flowlabel for tunnel */ -+ __u32 flags; /* tunnel flags */ -+ struct in6_addr laddr; /* local tunnel end-point address */ -+ struct in6_addr raddr; /* remote tunnel end-point address */ -+}; -+ -+#endif ---- linux-2.4.27/include/linux/rtnetlink.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/rtnetlink.h -@@ -315,6 +315,7 @@ - IFA_BROADCAST, - IFA_ANYCAST, - IFA_CACHEINFO, -+ IFA_HOMEAGENT, - __IFA_MAX - }; - -@@ -324,6 +325,7 @@ - - #define IFA_F_SECONDARY 0x01 - -+#define IFA_F_HOMEADDR 0x10 - #define IFA_F_DEPRECATED 0x20 - #define IFA_F_TENTATIVE 0x40 - #define IFA_F_PERMANENT 0x80 ---- linux-2.4.27/include/linux/skbuff.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/skbuff.h -@@ -177,7 +177,7 @@ - * want to keep them across layers you have to do a skb_clone() - * first. This is owned by whoever has the skb queued ATM. - */ -- char cb[48]; -+ char cb[64]; - - unsigned int len; /* Length of actual data */ - unsigned int data_len; ---- linux-2.4.27/include/linux/sysctl.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/linux/sysctl.h -@@ -404,6 +404,23 @@ - NET_IPV6_ICMP=19, - NET_IPV6_BINDV6ONLY=20, - NET_IPV6_MLD_MAX_MSF=25, -+ NET_IPV6_MOBILITY=26 -+}; -+ -+/* /proc/sys/net/ipv6/mobility */ -+enum { -+ NET_IPV6_MOBILITY_DEBUG=1, -+ NET_IPV6_MOBILITY_TUNNEL_SITELOCAL=2, -+ NET_IPV6_MOBILITY_ROUTER_SOLICITATION_MAX_SENDTIME=3, -+ NET_IPV6_MOBILITY_ROUTER_REACH=4, -+ NET_IPV6_MOBILITY_MDETECT_MECHANISM=5, -+ NET_IPV6_MOBILITY_RETROUT=6, -+ NET_IPV6_MOBILITY_MAX_TNLS=7, -+ NET_IPV6_MOBILITY_MIN_TNLS=8, -+ NET_IPV6_MOBILITY_BINDING_REFRESH=9, -+ NET_IPV6_MOBILITY_BU_F_LLADDR=10, -+ NET_IPV6_MOBILITY_BU_F_KEYMGM=11, -+ NET_IPV6_MOBILITY_BU_F_CN_ACK=12 - }; - - enum { ---- linux-2.4.27/include/net/addrconf.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/net/addrconf.h -@@ -16,9 +16,11 @@ - #if defined(__BIG_ENDIAN_BITFIELD) - __u8 onlink : 1, - autoconf : 1, -- reserved : 6; -+ router_address : 1, -+ reserved : 5; - #elif defined(__LITTLE_ENDIAN_BITFIELD) -- __u8 reserved : 6, -+ __u8 reserved : 5, -+ router_address : 1, - autoconf : 1, - onlink : 1; - #else -@@ -55,6 +57,7 @@ - struct net_device *dev); - extern struct inet6_ifaddr * ipv6_get_ifaddr(struct in6_addr *addr, - struct net_device *dev); -+extern void ipv6_del_addr(struct inet6_ifaddr *ifp); - extern int ipv6_get_saddr(struct dst_entry *dst, - struct in6_addr *daddr, - struct in6_addr *saddr); -@@ -85,7 +88,9 @@ - extern void ipv6_mc_down(struct inet6_dev *idev); - extern void ipv6_mc_init_dev(struct inet6_dev *idev); - extern void ipv6_mc_destroy_dev(struct inet6_dev *idev); -+extern void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); - extern void addrconf_dad_failure(struct inet6_ifaddr *ifp); -+extern void addrconf_dad_completed(struct inet6_ifaddr *ifp); - - extern int ipv6_chk_mcast_addr(struct net_device *dev, struct in6_addr *group, - struct in6_addr *src_addr); -@@ -116,6 +121,9 @@ - extern int register_inet6addr_notifier(struct notifier_block *nb); - extern int unregister_inet6addr_notifier(struct notifier_block *nb); - -+extern int ipv6_generate_eui64(u8 *eui, struct net_device *dev); -+extern int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev); -+ - static inline struct inet6_dev * - __in6_dev_get(struct net_device *dev) - { ---- linux-2.4.27/include/net/ip6_route.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/net/ip6_route.h -@@ -2,6 +2,7 @@ - #define _NET_IP6_ROUTE_H - - #define IP6_RT_PRIO_FW 16 -+#define IP6_RT_PRIO_MIPV6 64 - #define IP6_RT_PRIO_USER 1024 - #define IP6_RT_PRIO_ADDRCONF 256 - #define IP6_RT_PRIO_KERN 512 -@@ -40,6 +41,9 @@ - - extern int ip6_route_add(struct in6_rtmsg *rtmsg, - struct nlmsghdr *); -+ -+extern int ip6_route_del(struct in6_rtmsg *rtmsg, -+ struct nlmsghdr *); - extern int ip6_del_rt(struct rt6_info *, - struct nlmsghdr *); - -@@ -99,7 +103,8 @@ - */ - - static inline void ip6_dst_store(struct sock *sk, struct dst_entry *dst, -- struct in6_addr *daddr) -+ struct in6_addr *daddr, -+ struct in6_addr *saddr) - { - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - struct rt6_info *rt = (struct rt6_info *) dst; -@@ -107,6 +112,9 @@ - write_lock(&sk->dst_lock); - __sk_dst_set(sk, dst); - np->daddr_cache = daddr; -+#ifdef CONFIG_IPV6_SUBTREES -+ np->saddr_cache = saddr; -+#endif - np->dst_cookie = rt->rt6i_node ? rt->rt6i_node->fn_sernum : 0; - write_unlock(&sk->dst_lock); - } ---- linux-2.4.27/include/net/ipv6.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/net/ipv6.h -@@ -37,6 +37,7 @@ - #define NEXTHDR_ICMP 58 /* ICMP for IPv6. */ - #define NEXTHDR_NONE 59 /* No next header */ - #define NEXTHDR_DEST 60 /* Destination options header. */ -+#define NEXTHDR_MH 135 /* Mobility header, RFC 3775 */ - - #define NEXTHDR_MAX 255 - -@@ -146,9 +147,12 @@ - __u16 opt_flen; /* after fragment hdr */ - __u16 opt_nflen; /* before fragment hdr */ - -+ __u8 mipv6_flags; /* flags set by MIPv6 */ -+ - struct ipv6_opt_hdr *hopopt; - struct ipv6_opt_hdr *dst0opt; -- struct ipv6_rt_hdr *srcrt; /* Routing Header */ -+ struct ipv6_rt_hdr *srcrt; /* Routing Header Type 0 */ -+ struct ipv6_rt_hdr *srcrt2; /* Routing Header Type 2 */ - struct ipv6_opt_hdr *auth; - struct ipv6_opt_hdr *dst1opt; - -@@ -257,6 +261,38 @@ - a->s6_addr32[2] | a->s6_addr32[3] ) == 0); - } - -+static inline void ipv6_addr_prefix(struct in6_addr *pfx, -+ const struct in6_addr *addr, int plen) -+{ -+ /* caller must guarantee 0 <= plen <= 128 */ -+ int o = plen >> 3, -+ b = plen & 0x7; -+ -+ memcpy(pfx->s6_addr, addr, o); -+ if (b != 0) { -+ pfx->s6_addr[o] = addr->s6_addr[o] & (0xff00 >> b); -+ o++; -+ } -+ if (o < 16) -+ memset(pfx->s6_addr + o, 0, 16 - o); -+} -+ -+static inline int ipv6_prefix_cmp(const struct in6_addr *p1, -+ const struct in6_addr *p2, int plen) -+{ -+ int b = plen&0x7; -+ int o = plen>>3; -+ int res = 0; -+ -+ if (o > 0) -+ res = memcmp(&p1->s6_addr[0], &p2->s6_addr[0], o); -+ if (res == 0 && b > 0) { -+ __u8 m = (0xff00 >> b) & 0xff; -+ res = (p1->s6_addr[o] & m) - (p2->s6_addr[o] & m); -+ } -+ return res; -+} -+ - /* - * Prototypes exported by ipv6 - */ ---- /dev/null -+++ linux-2.4.27/include/net/ipv6_tunnel.h -@@ -0,0 +1,92 @@ -+/* -+ * $Id$ -+ */ -+ -+#ifndef _NET_IPV6_TUNNEL_H -+#define _NET_IPV6_TUNNEL_H -+ -+#include <linux/ipv6.h> -+#include <linux/netdevice.h> -+#include <linux/ipv6_tunnel.h> -+#include <linux/skbuff.h> -+#include <asm/atomic.h> -+ -+/* capable of sending packets */ -+#define IP6_TNL_F_CAP_XMIT 0x10000 -+/* capable of receiving packets */ -+#define IP6_TNL_F_CAP_RCV 0x20000 -+ -+#define IP6_TNL_MAX 128 -+ -+/* IPv6 tunnel */ -+ -+struct ip6_tnl { -+ struct ip6_tnl *next; /* next tunnel in list */ -+ struct net_device *dev; /* virtual device associated with tunnel */ -+ struct net_device_stats stat; /* statistics for tunnel device */ -+ int recursion; /* depth of hard_start_xmit recursion */ -+ struct ip6_tnl_parm parms; /* tunnel configuration paramters */ -+ struct flowi fl; /* flowi template for xmit */ -+ atomic_t refcnt; /* nr of identical tunnels used by kernel */ -+ struct socket *sock; -+}; -+ -+#define IP6_TNL_PRE_ENCAP 0 -+#define IP6_TNL_PRE_DECAP 1 -+#define IP6_TNL_MAXHOOKS 2 -+ -+#define IP6_TNL_DROP 0 -+#define IP6_TNL_ACCEPT 1 -+ -+typedef int ip6_tnl_hookfn(struct ip6_tnl *t, struct sk_buff *skb); -+ -+struct ip6_tnl_hook_ops { -+ struct list_head list; -+ unsigned int hooknum; -+ int priority; -+ ip6_tnl_hookfn *hook; -+}; -+ -+enum ip6_tnl_hook_priorities { -+ IP6_TNL_PRI_FIRST = INT_MIN, -+ IP6_TNL_PRI_LAST = INT_MAX -+}; -+ -+/* Tunnel encapsulation limit destination sub-option */ -+ -+struct ipv6_tlv_tnl_enc_lim { -+ __u8 type; /* type-code for option */ -+ __u8 length; /* option length */ -+ __u8 encap_limit; /* tunnel encapsulation limit */ -+} __attribute__ ((packed)); -+ -+#ifdef __KERNEL__ -+extern int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt); -+ -+extern struct ip6_tnl *ip6ip6_tnl_lookup(struct in6_addr *remote, -+ struct in6_addr *local); -+ -+void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p); -+ -+extern int ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p); -+ -+extern int ip6ip6_kernel_tnl_del(struct ip6_tnl *t); -+ -+extern unsigned int ip6ip6_tnl_inc_max_kdev_count(unsigned int n); -+ -+extern unsigned int ip6ip6_tnl_dec_max_kdev_count(unsigned int n); -+ -+extern unsigned int ip6ip6_tnl_inc_min_kdev_count(unsigned int n); -+ -+extern unsigned int ip6ip6_tnl_dec_min_kdev_count(unsigned int n); -+ -+extern void ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg); -+ -+extern void ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg); -+ -+#ifdef CONFIG_IPV6_TUNNEL -+extern int __init ip6_tunnel_init(void); -+extern void ip6_tunnel_cleanup(void); -+#endif -+#endif -+#endif ---- /dev/null -+++ linux-2.4.27/include/net/mipglue.h -@@ -0,0 +1,266 @@ -+/* -+ * Glue for Mobility support integration to IPv6 -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#ifndef _NET_MIPGLUE_H -+#define _NET_MIPGLUE_H -+ -+#ifndef USE_IPV6_MOBILITY -+#if defined(CONFIG_IPV6_MOBILITY) || defined(CONFIG_IPV6_MOBILITY_MODULE) -+#define USE_IPV6_MOBILITY -+#endif -+#endif -+ -+/* symbols to indicate whether destination options received should take -+ * effect or not (see exthdrs.c, procrcv.c) -+ */ -+#define MIPV6_DSTOPTS_ACCEPT 1 -+#define MIPV6_DSTOPTS_DISCARD 0 -+ -+#define MIPV6_IGN_RTR 0 -+#define MIPV6_ADD_RTR 1 -+#define MIPV6_CHG_RTR 2 -+ -+/* MIPV6: Approximate maximum for mobile IPv6 options and headers */ -+#define MIPV6_HEADERS 48 -+ -+#ifdef __KERNEL__ -+#include <net/mipv6.h> -+#include <linux/slab.h> -+#include <net/ipv6.h> -+ -+struct sk_buff; -+struct ndisc_options; -+struct sock; -+struct ipv6_txoptions; -+struct flowi; -+struct dst_entry; -+struct in6_addr; -+struct inet6_ifaddr; -+ -+#ifdef USE_IPV6_MOBILITY -+ -+/* calls a procedure from mipv6-module */ -+#define MIPV6_CALLPROC(X) if(mipv6_functions.X) mipv6_functions.X -+ -+/* calls a function from mipv6-module, default-value if function not defined -+ */ -+#define MIPV6_CALLFUNC(X,Y) (!mipv6_functions.X)?(Y):mipv6_functions.X -+ -+/* sets a handler-function to process a call */ -+#define MIPV6_SETCALL(X,Y) if(mipv6_functions.X) printk("mipv6: Warning, function assigned twice!\n"); \ -+ mipv6_functions.X = Y -+#define MIPV6_RESETCALL(X) mipv6_functions.X = NULL -+ -+/* pointers to mipv6 callable functions */ -+struct mipv6_callable_functions { -+ void (*mipv6_initialize_dstopt_rcv) (struct sk_buff *skb); -+ int (*mipv6_finalize_dstopt_rcv) (int process); -+ int (*mipv6_handle_homeaddr) (struct sk_buff *skb, int optoff); -+ int (*mipv6_ra_rcv) (struct sk_buff *skb, -+ struct ndisc_options *ndopts); -+ void (*mipv6_icmp_rcv) (struct sk_buff *skb); -+ struct ipv6_txoptions * (*mipv6_modify_txoptions) ( -+ struct sock *sk, -+ struct sk_buff *skb, -+ struct ipv6_txoptions *opt, -+ struct flowi *fl, -+ struct dst_entry **dst); -+ void (*mipv6_set_home) (int ifindex, struct in6_addr *homeaddr, -+ int plen, struct in6_addr *homeagent, -+ int plen2); -+ void (*mipv6_get_home_address) (struct in6_addr *home_addr); -+ void (*mipv6_get_care_of_address)(struct in6_addr *homeaddr, -+ struct in6_addr *coa); -+ int (*mipv6_is_home_addr)(struct in6_addr *addr); -+ void (*mipv6_change_router)(void); -+ void (*mipv6_check_dad)(struct in6_addr *home_addr); -+ void (*mipv6_icmp_swap_addrs)(struct sk_buff *skb); -+ int (*mipv6_forward)(struct sk_buff *skb); -+ int (*mipv6_mn_ha_probe)(struct inet6_ifaddr *ifp, u8 *lladdr); -+}; -+ -+extern struct mipv6_callable_functions mipv6_functions; -+ -+extern void mipv6_invalidate_calls(void); -+ -+extern int mipv6_handle_dstopt(struct sk_buff *skb, int optoff); -+ -+static inline int -+ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) -+{ -+ return MIPV6_CALLFUNC(mipv6_mn_ha_probe, 0)(ifp, lladdr); -+} -+ -+/* Must only be called for HA, no checks here */ -+static inline int ip6_mipv6_forward(struct sk_buff *skb) -+{ -+ return MIPV6_CALLFUNC(mipv6_forward, 0)(skb); -+} -+ -+/* -+ * Avoid adding new default routers if the old one is still in use -+ */ -+ -+static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb, -+ struct ndisc_options *ndopts) -+{ -+ return MIPV6_CALLFUNC(mipv6_ra_rcv, MIPV6_ADD_RTR)(skb, ndopts); -+} -+ -+static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr) -+{ -+ return MIPV6_CALLFUNC(mipv6_is_home_addr, 0)(addr); -+} -+ -+static inline void ndisc_mipv6_change_router(int change_rtr) -+{ -+ if (change_rtr == MIPV6_CHG_RTR) -+ MIPV6_CALLPROC(mipv6_change_router)(); -+} -+ -+static inline void ndisc_check_mipv6_dad(struct in6_addr *target) -+{ -+ MIPV6_CALLPROC(mipv6_check_dad)(target); -+} -+ -+static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb) -+{ -+ MIPV6_CALLPROC(mipv6_icmp_swap_addrs)(skb); -+} -+ -+static inline void mipv6_icmp_rcv(struct sk_buff *skb) -+{ -+ MIPV6_CALLPROC(mipv6_icmp_rcv)(skb); -+} -+ -+static inline int tcp_v6_get_mipv6_header_len(void) -+{ -+ return MIPV6_HEADERS; -+} -+ -+static inline struct in6_addr * -+mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr) -+{ -+ return daddr; -+} -+ -+static inline void -+addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen, -+ struct in6_addr *homeagent, int plen2) -+{ -+ MIPV6_CALLPROC(mipv6_set_home)(ifindex, homeaddr, plen, homeagent, plen2); -+} -+ -+static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr) -+{ -+ MIPV6_CALLPROC(mipv6_get_home_address)(saddr); -+} -+ -+static inline struct ipv6_txoptions * -+ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *opt, struct flowi *fl, -+ struct dst_entry **dst) -+{ -+ return MIPV6_CALLFUNC(mipv6_modify_txoptions, opt)(sk, skb, opt, fl, dst); -+ -+} -+ -+static inline void -+ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb) -+{ -+ struct inet6_skb_parm *opt; -+ if (txopt) { -+ opt = (struct inet6_skb_parm *)skb->cb; -+ opt->mipv6_flags = txopt->mipv6_flags; -+ } -+} -+ -+static inline void -+ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt, -+ struct ipv6_txoptions *orig_opt) -+{ -+ if (opt && opt != orig_opt) -+ kfree(opt); -+} -+ -+#else /* USE_IPV6_MOBILITY */ -+ -+#define mipv6_handle_dstopt ip6_tlvopt_unknown -+ -+static inline int -+ndisc_mip_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) -+{ -+ return 0; -+} -+ -+static inline int ip6_mipv6_forward(struct sk_buff *skb) -+{ -+ return 0; -+} -+ -+static inline int ndisc_mipv6_ra_rcv(struct sk_buff *skb, -+ struct ndisc_options *ndopts) -+{ -+ return MIPV6_ADD_RTR; -+} -+ -+static inline int ipv6_chk_mip_home_addr(struct in6_addr *addr) -+{ -+ return 0; -+} -+ -+static inline void ndisc_mipv6_change_router(int change_rtr) {} -+ -+static inline void ndisc_check_mipv6_dad(struct in6_addr *target) {} -+ -+static inline void icmpv6_swap_mipv6_addrs(struct sk_buff *skb) {} -+ -+static inline void mipv6_icmp_rcv(struct sk_buff *skb) {} -+ -+static inline int tcp_v6_get_mipv6_header_len(void) -+{ -+ return 0; -+} -+ -+static inline struct in6_addr * -+mipv6_get_fake_hdr_daddr(struct in6_addr *hdaddr, struct in6_addr *daddr) -+{ -+ return hdaddr; -+} -+ -+static inline void -+addrconf_set_mipv6_mn_home(int ifindex, struct in6_addr *homeaddr, int plen, -+ struct in6_addr *homeagent, int plen2) {} -+ -+static inline void addrconf_get_mipv6_home_address(struct in6_addr *saddr) {} -+ -+static inline struct ipv6_txoptions * -+ip6_add_mipv6_txoptions(struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *opt, struct flowi *fl, -+ struct dst_entry **dst) -+{ -+ return opt; -+} -+ -+static inline void -+ip6_mark_mipv6_packet(struct ipv6_txoptions *txopt, struct sk_buff *skb) {} -+ -+static inline void -+ip6_free_mipv6_txoptions(struct ipv6_txoptions *opt, -+ struct ipv6_txoptions *orig_opt) {} -+ -+#endif /* USE_IPV6_MOBILITY */ -+#endif /* __KERNEL__ */ -+#endif /* _NET_MIPGLUE_H */ ---- /dev/null -+++ linux-2.4.27/include/net/mipv6.h -@@ -0,0 +1,258 @@ -+/* -+ * Mobile IPv6 header-file -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#ifndef _NET_MIPV6_H -+#define _NET_MIPV6_H -+ -+#include <linux/types.h> -+#include <asm/byteorder.h> -+#include <linux/in6.h> -+ -+/* -+ * -+ * Mobile IPv6 Protocol constants -+ * -+ */ -+#define DHAAD_RETRIES 4 /* transmissions */ -+#define INITIAL_BINDACK_TIMEOUT 1 /* seconds */ -+#define INITIAL_DHAAD_TIMEOUT 3 /* seconds */ -+#define INITIAL_SOLICIT_TIMER 3 /* seconds */ -+#define MAX_BINDACK_TIMEOUT 32 /* seconds */ -+#define MAX_NONCE_LIFE 240 /* seconds */ -+#define MAX_TOKEN_LIFE 210 /* seconds */ -+#define MAX_RR_BINDING_LIFE 420 /* seconds */ -+#define MAX_UPDATE_RATE 3 /* 1/s (min delay=1s) */ -+#define PREFIX_ADV_RETRIES 3 /* transmissions */ -+#define PREFIX_ADV_TIMEOUT 3 /* seconds */ -+ -+#define MAX_FAST_UPDATES 5 /* transmissions */ -+#define MAX_PFX_ADV_DELAY 1000 /* seconds */ -+#define SLOW_UPDATE_RATE 10 /* 1/10s (max delay=10s)*/ -+#define INITIAL_BINDACK_DAD_TIMEOUT 2 /* seconds */ -+ -+/* -+ * -+ * Mobile IPv6 (RFC 3775) Protocol configuration variable defaults -+ * -+ */ -+#define DefHomeRtrAdvInterval 1000 /* seconds */ -+#define DefMaxMobPfxAdvInterval 86400 /* seconds */ -+#define DefMinDelayBetweenRAs 3 /* seconds (min 0.03) */ -+#define DefMinMobPfxAdvInterval 600 /* seconds */ -+#define DefInitialBindackTimeoutFirstReg 1.5 /* seconds */ -+ -+/* This is not actually specified in the draft, but is needed to avoid -+ * prefix solicitation storm when valid lifetime of a prefix is smaller -+ * than MAX_PFX_ADV_DELAY -+ */ -+#define MIN_PFX_SOL_DELAY 5 /* seconds */ -+ -+/* Mobile IPv6 ICMP types */ -+/* -+ * Official numbers from RFC 3775 -+ */ -+#define MIPV6_DHAAD_REQUEST 144 -+#define MIPV6_DHAAD_REPLY 145 -+#define MIPV6_PREFIX_SOLICIT 146 -+#define MIPV6_PREFIX_ADV 147 -+ -+/* Binding update flag codes */ -+#define MIPV6_BU_F_ACK 0x80 -+#define MIPV6_BU_F_HOME 0x40 -+#define MIPV6_BU_F_LLADDR 0x20 -+#define MIPV6_BU_F_KEYMGM 0x10 -+ -+/* Binding ackknowledgment flag codes */ -+#define MIPV6_BA_F_KEYMGM 0x80 -+ -+/* Binding error status */ -+#define MIPV6_BE_HAO_WO_BINDING 1 -+#define MIPV6_BE_UNKNOWN_MH_TYPE 2 -+ -+/* Mobility Header */ -+struct mipv6_mh -+{ -+ __u8 payload; /* Payload Protocol */ -+ __u8 length; /* MH Length */ -+ __u8 type; /* MH Type */ -+ __u8 reserved; /* Reserved */ -+ __u16 checksum; /* Checksum */ -+ __u8 data[0]; /* Message specific data */ -+} __attribute__ ((packed)); -+ -+/* Mobility Header type */ -+#define IPPROTO_MOBILITY 135 /* RFC 3775*/ -+/* Mobility Header Message Types */ -+ -+#define MIPV6_MH_BRR 0 -+#define MIPV6_MH_HOTI 1 -+#define MIPV6_MH_COTI 2 -+#define MIPV6_MH_HOT 3 -+#define MIPV6_MH_COT 4 -+#define MIPV6_MH_BU 5 -+#define MIPV6_MH_BA 6 -+#define MIPV6_MH_BE 7 -+ -+/* -+ * Status codes for Binding Acknowledgements -+ */ -+#define SUCCESS 0 -+#define REASON_UNSPECIFIED 128 -+#define ADMINISTRATIVELY_PROHIBITED 129 -+#define INSUFFICIENT_RESOURCES 130 -+#define HOME_REGISTRATION_NOT_SUPPORTED 131 -+#define NOT_HOME_SUBNET 132 -+#define NOT_HA_FOR_MN 133 -+#define DUPLICATE_ADDR_DETECT_FAIL 134 -+#define SEQUENCE_NUMBER_OUT_OF_WINDOW 135 -+#define EXPIRED_HOME_NONCE_INDEX 136 -+#define EXPIRED_CAREOF_NONCE_INDEX 137 -+#define EXPIRED_NONCES 138 -+#define REG_TYPE_CHANGE_FORBIDDEN 139 -+/* -+ * Values for mipv6_flags in struct inet6_skb_parm -+ */ -+ -+#define MIPV6_RCV_TUNNEL 0x1 -+#define MIPV6_SND_HAO 0x2 -+#define MIPV6_SND_BU 0x4 -+ -+/* -+ * Mobility Header Message structures -+ */ -+ -+struct mipv6_mh_brr -+{ -+ __u16 reserved; -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_bu -+{ -+ __u16 sequence; /* sequence number of BU */ -+ __u8 flags; /* flags */ -+ __u8 reserved; /* reserved bits */ -+ __u16 lifetime; /* lifetime of BU */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_ba -+{ -+ __u8 status; /* statuscode */ -+ __u8 reserved; /* reserved bits */ -+ __u16 sequence; /* sequence number of BA */ -+ __u16 lifetime; /* lifetime in CN's bcache */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_be -+{ -+ __u8 status; -+ __u8 reserved; -+ struct in6_addr home_addr; -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_addr_ti -+{ -+ __u16 reserved; /* Reserved */ -+ u_int8_t init_cookie[8]; /* HoT/CoT Init Cookie */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mh_addr_test -+{ -+ __u16 nonce_index; /* Home/Care-of Nonce Index */ -+ u_int8_t init_cookie[8]; /* HoT/CoT Init Cookie */ -+ u_int8_t kgen_token[8]; /* Home/Care-of key generation token */ -+ /* Mobility options */ -+} __attribute__ ((packed)); -+ -+/* -+ * Mobility Options for various MH types. -+ */ -+#define MIPV6_OPT_PAD1 0x00 -+#define MIPV6_OPT_PADN 0x01 -+#define MIPV6_OPT_BIND_REFRESH_ADVICE 0x02 -+#define MIPV6_OPT_ALTERNATE_COA 0x03 -+#define MIPV6_OPT_NONCE_INDICES 0x04 -+#define MIPV6_OPT_AUTH_DATA 0x05 -+ -+#define MIPV6_SEQ_GT(x,y) \ -+ ((short int)(((__u16)(x)) - ((__u16)(y))) > 0) -+ -+/* -+ * Mobility Option structures -+ */ -+ -+struct mipv6_mo -+{ -+ __u8 type; -+ __u8 length; -+ __u8 value[0]; /* type specific data */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_pad1 -+{ -+ __u8 type; -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_padn -+{ -+ __u8 type; -+ __u8 length; -+ __u8 data[0]; -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_alt_coa -+{ -+ __u8 type; -+ __u8 length; -+ struct in6_addr addr; /* alternate care-of-address */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_nonce_indices -+{ -+ __u8 type; -+ __u8 length; -+ __u16 home_nonce_i; /* Home Nonce Index */ -+ __u16 careof_nonce_i; /* Careof Nonce Index */ -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_bauth_data -+{ -+ __u8 type; -+ __u8 length; -+ __u8 data[0]; -+} __attribute__ ((packed)); -+ -+struct mipv6_mo_br_advice -+{ -+ __u8 type; -+ __u8 length; -+ __u16 refresh_interval; /* Refresh Interval */ -+} __attribute__ ((packed)); -+ -+/* -+ * Home Address Destination Option structure -+ */ -+struct mipv6_dstopt_homeaddr -+{ -+ __u8 type; /* type-code for option */ -+ __u8 length; /* option length */ -+ struct in6_addr addr; /* home address */ -+} __attribute__ ((packed)); -+ -+#endif /* _NET_MIPV6_H */ ---- linux-2.4.27/include/net/ndisc.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/net/ndisc.h -@@ -21,6 +21,10 @@ - #define ND_OPT_REDIRECT_HDR 4 - #define ND_OPT_MTU 5 - -+/* Mobile IPv6 specific ndisc options */ -+#define ND_OPT_RTR_ADV_INTERVAL 7 -+#define ND_OPT_HOME_AGENT_INFO 8 -+ - #define MAX_RTR_SOLICITATION_DELAY HZ - - #define ND_REACHABLE_TIME (30*HZ) -@@ -57,7 +61,7 @@ - } __attribute__((__packed__)); - - struct ndisc_options { -- struct nd_opt_hdr *nd_opt_array[7]; -+ struct nd_opt_hdr *nd_opt_array[10]; - struct nd_opt_hdr *nd_opt_piend; - }; - -@@ -67,6 +71,8 @@ - #define nd_opts_pi_end nd_opt_piend - #define nd_opts_rh nd_opt_array[ND_OPT_REDIRECT_HDR] - #define nd_opts_mtu nd_opt_array[ND_OPT_MTU] -+#define nd_opts_rai nd_opt_array[ND_OPT_RTR_ADV_INTERVAL] -+#define nd_opts_hai nd_opt_array[ND_OPT_HOME_AGENT_INFO] - - extern struct nd_opt_hdr *ndisc_next_option(struct nd_opt_hdr *cur, struct nd_opt_hdr *end); - extern struct ndisc_options *ndisc_parse_options(u8 *opt, int opt_len, struct ndisc_options *ndopts); -@@ -83,6 +89,15 @@ - struct in6_addr *daddr, - struct in6_addr *saddr); - -+extern void ndisc_send_na(struct net_device *dev, -+ struct neighbour *neigh, -+ struct in6_addr *daddr, -+ struct in6_addr *solicited_addr, -+ int router, -+ int solicited, -+ int override, -+ int inc_opt); -+ - extern void ndisc_send_rs(struct net_device *dev, - struct in6_addr *saddr, - struct in6_addr *daddr); ---- linux-2.4.27/include/net/sock.h~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/include/net/sock.h -@@ -149,7 +149,9 @@ - struct in6_addr rcv_saddr; - struct in6_addr daddr; - struct in6_addr *daddr_cache; -- -+#if defined(CONFIG_IPV6_SUBTREES) -+ struct in6_addr *saddr_cache; -+#endif - __u32 flow_label; - __u32 frag_size; - int hop_limit; ---- linux-2.4.27/net/Makefile~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/Makefile -@@ -7,7 +7,7 @@ - - O_TARGET := network.o - --mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp 802 -+mod-subdirs := ipv4/netfilter ipv6/netfilter ipx irda bluetooth atm netlink sched core sctp 802 ipv6 - export-objs := netsyms.o - - subdir-y := core ethernet -@@ -25,6 +25,7 @@ - ifneq ($(CONFIG_IPV6),n) - ifneq ($(CONFIG_IPV6),) - subdir-$(CONFIG_NETFILTER) += ipv6/netfilter -+subdir-$(CONFIG_IPV6_MOBILITY) += ipv6/mobile_ip6 - endif - endif - ---- linux-2.4.27/net/core/neighbour.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/core/neighbour.c -@@ -386,7 +386,7 @@ - if (!creat) - return NULL; - -- n = kmalloc(sizeof(*n) + key_len, GFP_KERNEL); -+ n = kmalloc(sizeof(*n) + key_len, GFP_ATOMIC); - if (n == NULL) - return NULL; - ---- linux-2.4.27/net/ipv6/Config.in~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/Config.in -@@ -1,10 +1,16 @@ - # - # IPv6 configuration - # -- -+bool ' IPv6: routing by source address (EXPERIMENTAL)' CONFIG_IPV6_SUBTREES - #bool ' IPv6: flow policy support' CONFIG_RT6_POLICY - #bool ' IPv6: firewall support' CONFIG_IPV6_FIREWALL - -+if [ "$CONFIG_IPV6" != "n" ]; then -+ dep_tristate ' IPv6: IPv6 over IPv6 Tunneling (EXPERIMENTAL)' CONFIG_IPV6_TUNNEL $CONFIG_IPV6 -+fi -+ -+source net/ipv6/mobile_ip6/Config.in -+ - if [ "$CONFIG_NETFILTER" != "n" ]; then - source net/ipv6/netfilter/Config.in - fi ---- linux-2.4.27/net/ipv6/Makefile~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/Makefile -@@ -6,18 +6,28 @@ - # unless it's something special (ie not a .c file). - # - -+export-objs := ipv6_syms.o ipv6_tunnel.o - --O_TARGET := ipv6.o -+#list-multi := ipv6.o ipv6_tunnel.o - --obj-y := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o sit.o \ -- route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o raw.o \ -- protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ -- exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ -- ip6_flowlabel.o ipv6_syms.o -+ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ -+ sit.o route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o \ -+ raw.o protocol.o icmp.o mcast.o reassembly.o tcp_ipv6.o \ -+ exthdrs.o sysctl_net_ipv6.o datagram.o proc.o \ -+ ip6_flowlabel.o ipv6_syms.o - --export-objs := ipv6_syms.o -+ifneq ($(CONFIG_IPV6_MOBILITY),n) -+ifneq ($(CONFIG_IPV6_MOBILITY),) -+ipv6-objs += mipglue.o -+endif -+endif -+ -+obj-$(CONFIG_IPV6) += ipv6.o -+obj-$(CONFIG_IPV6_TUNNEL) += ipv6_tunnel.o -+ -+ipv6.o: $(ipv6-objs) -+ $(LD) -r -o $@ $(ipv6-objs) - --obj-m := $(O_TARGET) - - #obj-$(CONFIG_IPV6_FIREWALL) += ip6_fw.o - ---- linux-2.4.27/net/ipv6/addrconf.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/addrconf.c -@@ -68,6 +68,8 @@ - - #include <asm/uaccess.h> - -+#include <net/mipglue.h> -+ - #define IPV6_MAX_ADDRESSES 16 - - /* Set to 3 to get tracing... */ -@@ -103,9 +105,9 @@ - - static int addrconf_ifdown(struct net_device *dev, int how); - --static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); -+void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags); - static void addrconf_dad_timer(unsigned long data); --static void addrconf_dad_completed(struct inet6_ifaddr *ifp); -+void addrconf_dad_completed(struct inet6_ifaddr *ifp); - static void addrconf_rs_timer(unsigned long data); - static void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifa); - -@@ -330,38 +332,6 @@ - return idev; - } - --void ipv6_addr_prefix(struct in6_addr *prefix, -- struct in6_addr *addr, int prefix_len) --{ -- unsigned long mask; -- int ncopy, nbits; -- -- memset(prefix, 0, sizeof(*prefix)); -- -- if (prefix_len <= 0) -- return; -- if (prefix_len > 128) -- prefix_len = 128; -- -- ncopy = prefix_len / 32; -- switch (ncopy) { -- case 4: prefix->s6_addr32[3] = addr->s6_addr32[3]; -- case 3: prefix->s6_addr32[2] = addr->s6_addr32[2]; -- case 2: prefix->s6_addr32[1] = addr->s6_addr32[1]; -- case 1: prefix->s6_addr32[0] = addr->s6_addr32[0]; -- case 0: break; -- } -- nbits = prefix_len % 32; -- if (nbits == 0) -- return; -- -- mask = ~((1 << (32 - nbits)) - 1); -- mask = htonl(mask); -- -- prefix->s6_addr32[ncopy] = addr->s6_addr32[ncopy] & mask; --} -- -- - static void dev_forward_change(struct inet6_dev *idev) - { - struct net_device *dev; -@@ -513,7 +483,7 @@ - - /* This function wants to get referenced ifp and releases it before return */ - --static void ipv6_del_addr(struct inet6_ifaddr *ifp) -+void ipv6_del_addr(struct inet6_ifaddr *ifp) - { - struct inet6_ifaddr *ifa, **ifap; - struct inet6_dev *idev = ifp->idev; -@@ -662,6 +632,12 @@ - if (match) - in6_ifa_put(match); - -+ /* The home address is always used as source address in -+ * MIPL mobile IPv6 -+ */ -+ if (scope != IFA_HOST && scope != IFA_LINK) -+ addrconf_get_mipv6_home_address(saddr); -+ - return err; - } - -@@ -815,7 +791,7 @@ - } - - --static int ipv6_generate_eui64(u8 *eui, struct net_device *dev) -+int ipv6_generate_eui64(u8 *eui, struct net_device *dev) - { - switch (dev->type) { - case ARPHRD_ETHER: -@@ -840,7 +816,7 @@ - return -1; - } - --static int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) -+int ipv6_inherit_eui64(u8 *eui, struct inet6_dev *idev) - { - int err = -1; - struct inet6_ifaddr *ifp; -@@ -1407,6 +1383,24 @@ - sit_route_add(dev); - } - -+/** -+ * addrconf_ipv6_tunnel_config - configure IPv6 tunnel device -+ * @dev: tunnel device -+ **/ -+ -+static void addrconf_ipv6_tunnel_config(struct net_device *dev) -+{ -+ struct inet6_dev *idev; -+ -+ ASSERT_RTNL(); -+ -+ /* Assign inet6_dev structure to tunnel device */ -+ if ((idev = ipv6_find_idev(dev)) == NULL) { -+ printk(KERN_DEBUG "init ipv6 tunnel: add_dev failed\n"); -+ return; -+ } -+} -+ - - int addrconf_notify(struct notifier_block *this, unsigned long event, - void * data) -@@ -1421,6 +1415,10 @@ - addrconf_sit_config(dev); - break; - -+ case ARPHRD_TUNNEL6: -+ addrconf_ipv6_tunnel_config(dev); -+ break; -+ - case ARPHRD_LOOPBACK: - init_loopback(dev); - break; -@@ -1602,7 +1600,7 @@ - /* - * Duplicate Address Detection - */ --static void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) -+void addrconf_dad_start(struct inet6_ifaddr *ifp, int flags) - { - struct net_device *dev; - unsigned long rand_num; -@@ -1667,7 +1665,7 @@ - in6_ifa_put(ifp); - } - --static void addrconf_dad_completed(struct inet6_ifaddr *ifp) -+void addrconf_dad_completed(struct inet6_ifaddr *ifp) - { - struct net_device * dev = ifp->idev->dev; - -@@ -1676,7 +1674,7 @@ - */ - - ipv6_ifa_notify(RTM_NEWADDR, ifp); -- -+ notifier_call_chain(&inet6addr_chain,NETDEV_UP,ifp); - /* If added prefix is link local and forwarding is off, - start sending router solicitations. - */ -@@ -1877,8 +1875,20 @@ - if (rta[IFA_LOCAL-1]) { - if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) - return -EINVAL; -+ if (ifm->ifa_flags & IFA_F_HOMEADDR && !rta[IFA_HOMEAGENT-1]) -+ return -EINVAL; - pfx = RTA_DATA(rta[IFA_LOCAL-1]); - } -+ if (rta[IFA_HOMEAGENT-1]) { -+ struct in6_addr *ha; -+ if (pfx == NULL || !(ifm->ifa_flags & IFA_F_HOMEADDR)) -+ return -EINVAL; -+ if (RTA_PAYLOAD(rta[IFA_HOMEAGENT-1]) < sizeof(*ha)) -+ return -EINVAL; -+ ha = RTA_DATA(rta[IFA_HOMEAGENT-1]); -+ addrconf_set_mipv6_mn_home(ifm->ifa_index, pfx, ifm->ifa_prefixlen, -+ ha, ifm->ifa_prefixlen); -+ } - if (pfx == NULL) - return -EINVAL; - ---- linux-2.4.27/net/ipv6/af_inet6.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/af_inet6.c -@@ -58,6 +58,9 @@ - #include <net/transp_v6.h> - #include <net/ip6_route.h> - #include <net/addrconf.h> -+#ifdef CONFIG_IPV6_TUNNEL -+#include <net/ipv6_tunnel.h> -+#endif - - #include <asm/uaccess.h> - #include <asm/system.h> -@@ -646,6 +649,11 @@ - err = ndisc_init(&inet6_family_ops); - if (err) - goto ndisc_fail; -+#ifdef CONFIG_IPV6_TUNNEL -+ err = ip6_tunnel_init(); -+ if (err) -+ goto ip6_tunnel_fail; -+#endif - err = igmp6_init(&inet6_family_ops); - if (err) - goto igmp_fail; -@@ -698,6 +706,10 @@ - #endif - igmp_fail: - ndisc_cleanup(); -+#ifdef CONFIG_IPV6_TUNNEL -+ ip6_tunnel_cleanup(); -+ip6_tunnel_fail: -+#endif - ndisc_fail: - icmpv6_cleanup(); - icmp_fail: -@@ -730,6 +742,9 @@ - ip6_route_cleanup(); - ipv6_packet_cleanup(); - igmp6_cleanup(); -+#ifdef CONFIG_IPV6_TUNNEL -+ ip6_tunnel_cleanup(); -+#endif - ndisc_cleanup(); - icmpv6_cleanup(); - #ifdef CONFIG_SYSCTL ---- linux-2.4.27/net/ipv6/exthdrs.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/exthdrs.c -@@ -41,6 +41,9 @@ - #include <net/ip6_route.h> - #include <net/addrconf.h> - -+#include <net/mipglue.h> -+#include <net/mipv6.h> -+ - #include <asm/uaccess.h> - - /* -@@ -160,7 +163,8 @@ - *****************************/ - - struct tlvtype_proc tlvprocdestopt_lst[] = { -- /* No destination options are defined now */ -+ /* Mobility Support destination options */ -+ {MIPV6_TLV_HOMEADDR, mipv6_handle_dstopt}, - {-1, NULL} - }; - -@@ -210,6 +214,7 @@ - - struct ipv6_rt_hdr *hdr; - struct rt0_hdr *rthdr; -+ struct rt2_hdr *rt2hdr; - - if (!pskb_may_pull(skb, (skb->h.raw-skb->data)+8) || - !pskb_may_pull(skb, (skb->h.raw-skb->data)+((skb->h.raw[1]+1)<<3))) { -@@ -225,17 +230,25 @@ - kfree_skb(skb); - return -1; - } -- -+ /* Silently discard invalid packets containing RTH type 2 */ -+ if (hdr->type == IPV6_SRCRT_TYPE_2 && -+ (hdr->hdrlen != 2 || hdr->segments_left != 1)) { -+ kfree_skb(skb); -+ return -1; -+ } - looped_back: - if (hdr->segments_left == 0) { -- opt->srcrt = skb->h.raw - skb->nh.raw; -+ if (hdr->type == IPV6_SRCRT_TYPE_0) -+ opt->srcrt = skb->h.raw - skb->nh.raw; -+ else if (hdr->type == IPV6_SRCRT_TYPE_2) -+ opt->srcrt2 = skb->h.raw - skb->nh.raw; - skb->h.raw += (hdr->hdrlen + 1) << 3; - opt->dst0 = opt->dst1; - opt->dst1 = 0; - return (&hdr->nexthdr) - skb->nh.raw; - } - -- if (hdr->type != IPV6_SRCRT_TYPE_0) { -+ if (hdr->type != IPV6_SRCRT_TYPE_0 && hdr->type != IPV6_SRCRT_TYPE_2) { - icmpv6_param_prob(skb, ICMPV6_HDR_FIELD, (&hdr->type) - skb->nh.raw); - return -1; - } -@@ -275,9 +288,20 @@ - - i = n - --hdr->segments_left; - -- rthdr = (struct rt0_hdr *) hdr; -- addr = rthdr->addr; -- addr += i - 1; -+ if (hdr->type == IPV6_SRCRT_TYPE_0) { -+ rthdr = (struct rt0_hdr *) hdr; -+ addr = rthdr->addr; -+ addr += i - 1; -+ } else { -+ /* check that address is this node's home address */ -+ rt2hdr = (struct rt2_hdr *) hdr; -+ addr = &rt2hdr->addr; -+ if (!ipv6_chk_addr(addr, NULL) || -+ !ipv6_chk_mip_home_addr(addr)) { -+ kfree_skb(skb); -+ return -1; -+ } -+ } - - addr_type = ipv6_addr_type(addr); - -@@ -330,6 +354,10 @@ - temporary (or permanent) backdoor. - If listening socket set IPV6_RTHDR to 2, then we invert header. - --ANK (980729) -+ -+ By the Mobile IPv6 specification Type 2 routing header MUST NOT be -+ inverted. -+ --AJT (20020917) - */ - - struct ipv6_txoptions * -@@ -352,6 +380,18 @@ - struct ipv6_txoptions *opt; - int hdrlen = ipv6_optlen(hdr); - -+ if (hdr->type == IPV6_SRCRT_TYPE_2) { -+ opt = sock_kmalloc(sk, sizeof(*opt) + hdrlen, GFP_ATOMIC); -+ if (opt == NULL) -+ return NULL; -+ memset(opt, 0, sizeof(*opt)); -+ opt->tot_len = sizeof(*opt) + hdrlen; -+ opt->srcrt = (void*)(opt+1); -+ opt->opt_nflen = hdrlen; -+ memcpy(opt->srcrt, hdr, sizeof(struct rt2_hdr)); -+ return opt; -+ } -+ - if (hdr->segments_left || - hdr->type != IPV6_SRCRT_TYPE_0 || - hdr->hdrlen & 0x01) -@@ -622,8 +662,18 @@ - if (opt) { - if (opt->dst0opt) - prev_hdr = ipv6_build_exthdr(skb, prev_hdr, NEXTHDR_DEST, opt->dst0opt); -- if (opt->srcrt) -- prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); -+ if (opt->srcrt) { -+ if (opt->srcrt2) { -+ struct in6_addr *rt2_hop = &((struct rt2_hdr *)opt->srcrt2)->addr; -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, rt2_hop); -+ } else -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt, daddr); -+ } -+ if (opt->srcrt2) { -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; -+ ipv6_addr_copy(&parm->hoa, daddr); -+ prev_hdr = ipv6_build_rthdr(skb, prev_hdr, opt->srcrt2, daddr); -+ } - } - return prev_hdr; - } -@@ -684,6 +734,11 @@ - u8 *proto, - struct in6_addr **daddr) - { -+ if (opt->srcrt2) { -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *)skb->cb; -+ ipv6_addr_copy(&parm->hoa, *daddr); -+ ipv6_push_rthdr(skb, proto, opt->srcrt2, daddr); -+ } - if (opt->srcrt) - ipv6_push_rthdr(skb, proto, opt->srcrt, daddr); - if (opt->dst0opt) -@@ -719,6 +774,8 @@ - *((char**)&opt2->auth) += dif; - if (opt2->srcrt) - *((char**)&opt2->srcrt) += dif; -+ if (opt2->srcrt2) -+ *((char**)&opt2->srcrt2) += dif; - } - return opt2; - } ---- linux-2.4.27/net/ipv6/icmp.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/icmp.c -@@ -61,6 +61,8 @@ - #include <net/addrconf.h> - #include <net/icmp.h> - -+#include <net/mipglue.h> -+ - #include <asm/uaccess.h> - #include <asm/system.h> - -@@ -364,6 +366,8 @@ - - msg.len = len; - -+ icmpv6_swap_mipv6_addrs(skb); -+ - ip6_build_xmit(sk, icmpv6_getfrag, &msg, &fl, len, NULL, -1, - MSG_DONTWAIT); - if (type >= ICMPV6_DEST_UNREACH && type <= ICMPV6_PARAMPROB) -@@ -562,13 +566,13 @@ - rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev, - ntohl(hdr->icmp6_mtu)); - -- /* -- * Drop through to notify -- */ -+ icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); -+ break; - - case ICMPV6_DEST_UNREACH: -- case ICMPV6_TIME_EXCEED: - case ICMPV6_PARAMPROB: -+ mipv6_icmp_rcv(skb); -+ case ICMPV6_TIME_EXCEED: - icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu); - break; - -@@ -598,6 +602,13 @@ - case ICMPV6_MLD2_REPORT: - break; - -+ case MIPV6_DHAAD_REQUEST: -+ case MIPV6_DHAAD_REPLY: -+ case MIPV6_PREFIX_SOLICIT: -+ case MIPV6_PREFIX_ADV: -+ mipv6_icmp_rcv(skb); -+ break; -+ - default: - if (net_ratelimit()) - printk(KERN_DEBUG "icmpv6: msg of unkown type\n"); ---- linux-2.4.27/net/ipv6/ip6_fib.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/ip6_fib.c -@@ -18,6 +18,7 @@ - * Yuji SEKIYA @USAGI: Support default route on router node; - * remove ip6_null_entry from the top of - * routing table. -+ * Ville Nuorvala: Fixes to source address based routing - */ - #include <linux/config.h> - #include <linux/errno.h> -@@ -40,7 +41,6 @@ - #include <net/ip6_route.h> - - #define RT6_DEBUG 2 --#undef CONFIG_IPV6_SUBTREES - - #if RT6_DEBUG >= 3 - #define RT6_TRACE(x...) printk(KERN_DEBUG x) -@@ -500,6 +500,8 @@ - mod_timer(&ip6_fib_timer, jiffies + ip6_rt_gc_interval); - } - -+static struct rt6_info * fib6_find_prefix(struct fib6_node *fn); -+ - /* - * Add routing information to the routing tree. - * <destination addr>/<source addr> -@@ -508,17 +510,19 @@ - - int fib6_add(struct fib6_node *root, struct rt6_info *rt, struct nlmsghdr *nlh) - { -- struct fib6_node *fn; -+ struct fib6_node *fn = root; - int err = -ENOMEM; - -- fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), -- rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); -+#ifdef CONFIG_IPV6_SUBTREES -+ struct fib6_node *pn = NULL; - -+ fn = fib6_add_1(root, &rt->rt6i_src.addr, sizeof(struct in6_addr), -+ rt->rt6i_src.plen, (u8*) &rt->rt6i_src - (u8*) rt); -+ - if (fn == NULL) - goto out; - --#ifdef CONFIG_IPV6_SUBTREES -- if (rt->rt6i_src.plen) { -+ if (rt->rt6i_dst.plen) { - struct fib6_node *sn; - - if (fn->subtree == NULL) { -@@ -546,9 +550,9 @@ - - /* Now add the first leaf node to new subtree */ - -- sn = fib6_add_1(sfn, &rt->rt6i_src.addr, -- sizeof(struct in6_addr), rt->rt6i_src.plen, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ sn = fib6_add_1(sfn, &rt->rt6i_dst.addr, -+ sizeof(struct in6_addr), rt->rt6i_dst.plen, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - - if (sn == NULL) { - /* If it is failed, discard just allocated -@@ -562,21 +566,30 @@ - /* Now link new subtree to main tree */ - sfn->parent = fn; - fn->subtree = sfn; -- if (fn->leaf == NULL) { -- fn->leaf = rt; -- atomic_inc(&rt->rt6i_ref); -- } - } else { -- sn = fib6_add_1(fn->subtree, &rt->rt6i_src.addr, -- sizeof(struct in6_addr), rt->rt6i_src.plen, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ sn = fib6_add_1(fn->subtree, &rt->rt6i_dst.addr, -+ sizeof(struct in6_addr), rt->rt6i_dst.plen, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - - if (sn == NULL) - goto st_failure; - } - -+ /* fib6_add_1 might have cleared the old leaf pointer */ -+ if (fn->leaf == NULL) { -+ fn->leaf = rt; -+ atomic_inc(&rt->rt6i_ref); -+ } -+ -+ pn = fn; - fn = sn; - } -+#else -+ fn = fib6_add_1(root, &rt->rt6i_dst.addr, sizeof(struct in6_addr), -+ rt->rt6i_dst.plen, (u8*) &rt->rt6i_dst - (u8*) rt); -+ -+ if (fn == NULL) -+ goto out; - #endif - - err = fib6_add_rt2node(fn, rt, nlh); -@@ -588,8 +601,30 @@ - } - - out: -- if (err) -+ if (err) { -+#ifdef CONFIG_IPV6_SUBTREES -+ -+ /* If fib6_add_1 has cleared the old leaf pointer in the -+ super-tree leaf node, we have to find a new one for it. -+ -+ This situation will never arise in the sub-tree since -+ the node will at least have the route that caused -+ fib6_add_rt2node to fail. -+ */ -+ -+ if (pn && !(pn->fn_flags & RTN_RTINFO)) { -+ pn->leaf = fib6_find_prefix(pn); -+#if RT6_DEBUG >= 2 -+ if (!pn->leaf) { -+ BUG_TRAP(pn->leaf); -+ pn->leaf = &ip6_null_entry; -+ } -+#endif -+ atomic_inc(&pn->leaf->rt6i_ref); -+ } -+#endif - dst_free(&rt->u.dst); -+ } - return err; - - #ifdef CONFIG_IPV6_SUBTREES -@@ -597,8 +632,8 @@ - is orphan. If it is, shoot it. - */ - st_failure: -- if (fn && !(fn->fn_flags&RTN_RTINFO|RTN_ROOT)) -- fib_repair_tree(fn); -+ if (fn && !(fn->fn_flags & (RTN_RTINFO | RTN_ROOT))) -+ fib6_repair_tree(fn); - dst_free(&rt->u.dst); - return err; - #endif -@@ -641,22 +676,28 @@ - break; - } - -- while ((fn->fn_flags & RTN_ROOT) == 0) { -+ for (;;) { - #ifdef CONFIG_IPV6_SUBTREES - if (fn->subtree) { -- struct fib6_node *st; -- struct lookup_args *narg; -- -- narg = args + 1; -- -- if (narg->addr) { -- st = fib6_lookup_1(fn->subtree, narg); -+ struct rt6key *key; - -- if (st && !(st->fn_flags & RTN_ROOT)) -- return st; -+ key = (struct rt6key *) ((u8 *) fn->leaf + -+ args->offset); -+ -+ if (addr_match(&key->addr, args->addr, key->plen)) { -+ struct fib6_node *st; -+ struct lookup_args *narg = args + 1; -+ if (!ipv6_addr_any(narg->addr)) { -+ st = fib6_lookup_1(fn->subtree, narg); -+ -+ if (st && !(st->fn_flags & RTN_ROOT)) -+ return st; -+ } - } - } - #endif -+ if (fn->fn_flags & RTN_ROOT) -+ break; - - if (fn->fn_flags & RTN_RTINFO) { - struct rt6key *key; -@@ -680,13 +721,22 @@ - struct lookup_args args[2]; - struct rt6_info *rt = NULL; - struct fib6_node *fn; -+#ifdef CONFIG_IPV6_SUBTREES -+ struct in6_addr saddr_buf; -+#endif - -+#ifdef CONFIG_IPV6_SUBTREES -+ if (saddr == NULL) { -+ memset(&saddr_buf, 0, sizeof(struct in6_addr)); -+ saddr = &saddr_buf; -+ } -+ args[0].offset = (u8*) &rt->rt6i_src - (u8*) rt; -+ args[0].addr = saddr; -+ args[1].offset = (u8*) &rt->rt6i_dst - (u8*) rt; -+ args[1].addr = daddr; -+#else - args[0].offset = (u8*) &rt->rt6i_dst - (u8*) rt; - args[0].addr = daddr; -- --#ifdef CONFIG_IPV6_SUBTREES -- args[1].offset = (u8*) &rt->rt6i_src - (u8*) rt; -- args[1].addr = saddr; - #endif - - fn = fib6_lookup_1(root, args); -@@ -739,19 +789,25 @@ - { - struct rt6_info *rt = NULL; - struct fib6_node *fn; -- -- fn = fib6_locate_1(root, daddr, dst_len, -- (u8*) &rt->rt6i_dst - (u8*) rt); -- - #ifdef CONFIG_IPV6_SUBTREES -- if (src_len) { -- BUG_TRAP(saddr!=NULL); -- if (fn == NULL) -- fn = fn->subtree; -+ struct in6_addr saddr_buf; -+ -+ if (saddr == NULL) { -+ memset(&saddr_buf, 0, sizeof(struct in6_addr)); -+ saddr = &saddr_buf; -+ } -+ fn = fib6_locate_1(root, saddr, src_len, -+ (u8*) &rt->rt6i_src - (u8*) rt); -+ if (dst_len) { - if (fn) -- fn = fib6_locate_1(fn, saddr, src_len, -- (u8*) &rt->rt6i_src - (u8*) rt); -+ fn = fib6_locate_1(fn->subtree, daddr, dst_len, -+ (u8*) &rt->rt6i_dst - (u8*) rt); -+ else -+ return NULL; - } -+#else -+ fn = fib6_locate_1(root, daddr, dst_len, -+ (u8*) &rt->rt6i_dst - (u8*) rt); - #endif - - if (fn && fn->fn_flags&RTN_RTINFO) -@@ -939,7 +995,7 @@ - } - fn = fn->parent; - } -- /* No more references are possiible at this point. */ -+ /* No more references are possible at this point. */ - if (atomic_read(&rt->rt6i_ref) != 1) BUG(); - } - ---- linux-2.4.27/net/ipv6/ip6_input.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/ip6_input.c -@@ -40,13 +40,42 @@ - #include <net/ip6_route.h> - #include <net/addrconf.h> - -+static inline int ip6_proxy_chk(struct sk_buff *skb) -+{ -+ struct ipv6hdr *hdr = skb->nh.ipv6h; -+ -+ if (ipv6_addr_type(&hdr->daddr)&IPV6_ADDR_UNICAST && -+ pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { -+ u8 nexthdr = hdr->nexthdr; -+ int offset; -+ struct icmp6hdr msg; - -+ if (ipv6_ext_hdr(nexthdr)) { -+ offset = ipv6_skip_exthdr(skb, sizeof(*hdr), &nexthdr, -+ skb->len - sizeof(*hdr)); -+ if (offset < 0) -+ return 0; -+ } else -+ offset = sizeof(*hdr); -+ -+ /* capture unicast NUD probes on behalf of the proxied node */ - -+ if (nexthdr == IPPROTO_ICMPV6 && -+ !skb_copy_bits(skb, offset, &msg, sizeof(msg)) && -+ msg.icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) { -+ return 1; -+ } -+ } -+ return 0; -+} -+ - static inline int ip6_rcv_finish( struct sk_buff *skb) - { -- if (skb->dst == NULL) -- ip6_route_input(skb); -- -+ if (skb->dst == NULL) { -+ if (ip6_proxy_chk(skb)) -+ return ip6_input(skb); -+ ip6_route_input(skb); -+ } - return skb->dst->input(skb); - } - ---- linux-2.4.27/net/ipv6/ip6_output.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/ip6_output.c -@@ -50,6 +50,8 @@ - #include <net/rawv6.h> - #include <net/icmp.h> - -+#include <net/mipglue.h> -+ - static __inline__ void ipv6_select_ident(struct sk_buff *skb, struct frag_hdr *fhdr) - { - static u32 ipv6_fragmentation_id = 1; -@@ -194,7 +196,14 @@ - u8 proto = fl->proto; - int seg_len = skb->len; - int hlimit; -+ int retval; -+ struct ipv6_txoptions *orig_opt = opt; -+ -+ opt = ip6_add_mipv6_txoptions(sk, skb, orig_opt, fl, &dst); - -+ if(orig_opt && !opt) -+ return -ENOMEM; -+ - if (opt) { - int head_room; - -@@ -209,8 +218,11 @@ - struct sk_buff *skb2 = skb_realloc_headroom(skb, head_room); - kfree_skb(skb); - skb = skb2; -- if (skb == NULL) -+ if (skb == NULL) { -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return -ENOBUFS; -+ } - if (sk) - skb_set_owner_w(skb, sk); - } -@@ -242,7 +254,10 @@ - - if (skb->len <= dst->pmtu) { - IP6_INC_STATS(Ip6OutRequests); -- return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); -+ ip6_mark_mipv6_packet(opt, skb); -+ retval = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ return retval; - } - - if (net_ratelimit()) -@@ -250,6 +265,9 @@ - skb->dev = dst->dev; - icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, dst->pmtu, skb->dev); - kfree_skb(skb); -+ -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return -EMSGSIZE; - } - -@@ -473,6 +491,7 @@ - - IP6_INC_STATS(Ip6FragCreates); - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, skb); - err = NF_HOOK(PF_INET6,NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); - if (err) { - kfree_skb(last_skb); -@@ -499,6 +518,7 @@ - IP6_INC_STATS(Ip6FragCreates); - IP6_INC_STATS(Ip6FragOKs); - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, last_skb); - return NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, last_skb, NULL,dst->dev, ip6_maybe_reroute); - } - -@@ -509,26 +529,43 @@ - struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; - struct in6_addr *final_dst = NULL; - struct dst_entry *dst; -+ struct rt6_info *rt; - int err = 0; - unsigned int pktlength, jumbolen, mtu; - struct in6_addr saddr; -+ struct ipv6_txoptions *orig_opt = opt; -+#ifdef CONFIG_IPV6_SUBTREES -+ struct dst_entry *org_dst; -+#endif -+ -+ opt = ip6_add_mipv6_txoptions(sk, NULL, orig_opt, fl, NULL); -+ -+ if(orig_opt && !opt) -+ return -ENOMEM; - - if (opt && opt->srcrt) { - struct rt0_hdr *rt0 = (struct rt0_hdr *) opt->srcrt; - final_dst = fl->fl6_dst; - fl->fl6_dst = rt0->addr; -- } -+ } else if (opt && opt->srcrt2) { -+ struct rt2_hdr *rt2 = (struct rt2_hdr *) opt->srcrt2; -+ final_dst = fl->fl6_dst; -+ fl->fl6_dst = &rt2->addr; -+ } - - if (!fl->oif && ipv6_addr_is_multicast(fl->nl_u.ip6_u.daddr)) - fl->oif = np->mcast_oif; - - dst = __sk_dst_check(sk, np->dst_cookie); -+#ifdef CONFIG_IPV6_SUBTREES -+ org_dst = dst; -+#endif - if (dst) { -- struct rt6_info *rt = (struct rt6_info*)dst; -+ rt = (struct rt6_info*)dst; - - /* Yes, checking route validity in not connected - case is not very simple. Take into account, -- that we do not support routing by source, TOS, -+ that we do not support routing by TOS, - and MSG_DONTROUTE --ANK (980726) - - 1. If route was host route, check that -@@ -548,6 +585,13 @@ - ipv6_addr_cmp(fl->fl6_dst, &rt->rt6i_dst.addr)) - && (np->daddr_cache == NULL || - ipv6_addr_cmp(fl->fl6_dst, np->daddr_cache))) -+#ifdef CONFIG_IPV6_SUBTREES -+ || (fl->fl6_src != NULL -+ && (rt->rt6i_src.plen != 128 || -+ ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) -+ && (np->saddr_cache == NULL || -+ ipv6_addr_cmp(fl->fl6_src, np->saddr_cache))) -+#endif - || (fl->oif && fl->oif != dst->dev->ifindex)) { - dst = NULL; - } else -@@ -560,21 +604,42 @@ - if (dst->error) { - IP6_INC_STATS(Ip6OutNoRoutes); - dst_release(dst); -+ ip6_free_mipv6_txoptions(opt, orig_opt); - return -ENETUNREACH; - } - - if (fl->fl6_src == NULL) { - err = ipv6_get_saddr(dst, fl->fl6_dst, &saddr); -- - if (err) { - #if IP6_DEBUG >= 2 - printk(KERN_DEBUG "ip6_build_xmit: " - "no available source address\n"); - #endif -+ -+#ifdef CONFIG_IPV6_SUBTREES -+ if (dst != org_dst) { -+ dst_release(dst); -+ dst = org_dst; -+ } -+#endif - goto out; - } - fl->fl6_src = &saddr; - } -+#ifdef CONFIG_IPV6_SUBTREES -+ rt = (struct rt6_info*)dst; -+ if (dst != org_dst || rt->rt6i_src.plen != 128 || -+ ipv6_addr_cmp(fl->fl6_src, &rt->rt6i_src.addr)) { -+ dst_release(dst); -+ dst = ip6_route_output(sk, fl); -+ if (dst->error) { -+ IP6_INC_STATS(Ip6OutNoRoutes); -+ dst_release(dst); -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ return -ENETUNREACH; -+ } -+ } -+#endif - pktlength = length; - - if (hlimit < 0) { -@@ -667,6 +732,7 @@ - - if (!err) { - IP6_INC_STATS(Ip6OutRequests); -+ ip6_mark_mipv6_packet(opt, skb); - err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, ip6_maybe_reroute); - } else { - err = -EFAULT; -@@ -688,9 +754,14 @@ - * cleanup - */ - out: -- ip6_dst_store(sk, dst, fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL); -+ ip6_dst_store(sk, dst, -+ fl->nl_u.ip6_u.daddr == &np->daddr ? &np->daddr : NULL, -+ fl->nl_u.ip6_u.saddr == &np->saddr ? &np->saddr : NULL); - if (err > 0) - err = np->recverr ? net_xmit_errno(err) : 0; -+ -+ ip6_free_mipv6_txoptions(opt, orig_opt); -+ - return err; - } - -@@ -769,6 +840,15 @@ - return -ETIMEDOUT; - } - -+ /* The proxying router can't forward traffic sent to a link-local -+ address, so signal the sender and discard the packet. This -+ behavior is required by the MIPv6 specification. */ -+ -+ if (ipv6_addr_type(&hdr->daddr) & IPV6_ADDR_LINKLOCAL && -+ skb->dev && pneigh_lookup(&nd_tbl, &hdr->daddr, skb->dev, 0)) { -+ dst_link_failure(skb); -+ goto drop; -+ } - /* IPv6 specs say nothing about it, but it is clear that we cannot - send redirects to source routed frames. - */ ---- linux-2.4.27/net/ipv6/ipv6_syms.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/ipv6_syms.c -@@ -6,6 +6,8 @@ - #include <net/ipv6.h> - #include <net/addrconf.h> - #include <net/ip6_route.h> -+#include <net/ndisc.h> -+#include <net/mipglue.h> - - EXPORT_SYMBOL(ipv6_addr_type); - EXPORT_SYMBOL(icmpv6_send); -@@ -35,3 +37,47 @@ - EXPORT_SYMBOL(in6_dev_finish_destroy); - EXPORT_SYMBOL(ipv6_skip_exthdr); - -+#if defined(CONFIG_IPV6_TUNNEL_MODULE) || defined(CONFIG_IPV6_MOBILITY_MODULE) -+EXPORT_SYMBOL(ip6_build_xmit); -+EXPORT_SYMBOL(rt6_lookup); -+EXPORT_SYMBOL(ipv6_ext_hdr); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MODULE -+EXPORT_SYMBOL(mipv6_functions); -+EXPORT_SYMBOL(mipv6_invalidate_calls); -+#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE) -+EXPORT_SYMBOL(ip6_route_add); -+EXPORT_SYMBOL(ip6_route_del); -+EXPORT_SYMBOL(ipv6_get_lladdr); -+EXPORT_SYMBOL(ipv6_get_ifaddr); -+EXPORT_SYMBOL(nd_tbl); -+EXPORT_SYMBOL(ndisc_send_ns); -+EXPORT_SYMBOL(ndisc_send_na); -+EXPORT_SYMBOL(ndisc_next_option); -+EXPORT_SYMBOL(inet6_ifa_finish_destroy); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE -+EXPORT_SYMBOL(ipv6_dev_ac_dec); -+EXPORT_SYMBOL(ipv6_dev_ac_inc); -+EXPORT_SYMBOL(ipv6_dev_mc_dec); -+EXPORT_SYMBOL(ipv6_dev_mc_inc); -+EXPORT_SYMBOL(ip6_forward); -+EXPORT_SYMBOL(ip6_input); -+EXPORT_SYMBOL(ipv6_chk_acast_addr); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE -+#endif -+EXPORT_SYMBOL(addrconf_add_ifaddr); -+EXPORT_SYMBOL(addrconf_del_ifaddr); -+EXPORT_SYMBOL(addrconf_dad_start); -+EXPORT_SYMBOL(ip6_del_rt); -+EXPORT_SYMBOL(ip6_routing_table); -+EXPORT_SYMBOL(rt6_get_dflt_router); -+EXPORT_SYMBOL(rt6_purge_dflt_routers); -+EXPORT_SYMBOL(rt6_lock); -+EXPORT_SYMBOL(ndisc_send_rs); -+EXPORT_SYMBOL(fib6_clean_tree); -+EXPORT_SYMBOL(ipv6_del_addr); -+EXPORT_SYMBOL(ipv6_generate_eui64); -+EXPORT_SYMBOL(ipv6_inherit_eui64); -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/ipv6_tunnel.c -@@ -0,0 +1,1604 @@ -+/* -+ * IPv6 over IPv6 tunnel device -+ * Linux INET6 implementation -+ * -+ * Authors: -+ * Ville Nuorvala <vnuorval@tcs.hut.fi> -+ * -+ * $Id$ -+ * -+ * Based on: -+ * linux/net/ipv6/sit.c -+ * -+ * RFC 2473 -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/errno.h> -+#include <linux/types.h> -+#include <linux/socket.h> -+#include <linux/sockios.h> -+#include <linux/if.h> -+#include <linux/in.h> -+#include <linux/ip.h> -+#include <linux/if_tunnel.h> -+#include <linux/net.h> -+#include <linux/in6.h> -+#include <linux/netdevice.h> -+#include <linux/if_arp.h> -+#include <linux/icmpv6.h> -+#include <linux/init.h> -+#include <linux/route.h> -+#include <linux/rtnetlink.h> -+#include <linux/tqueue.h> -+ -+#include <asm/uaccess.h> -+#include <asm/atomic.h> -+ -+#include <net/sock.h> -+#include <net/ipv6.h> -+#include <net/protocol.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/ipv6_tunnel.h> -+ -+MODULE_AUTHOR("Ville Nuorvala"); -+MODULE_DESCRIPTION("IPv6-in-IPv6 tunnel"); -+MODULE_LICENSE("GPL"); -+ -+#define IPV6_TLV_TEL_DST_SIZE 8 -+ -+#ifdef IP6_TNL_DEBUG -+#define IP6_TNL_TRACE(x...) printk(KERN_DEBUG "%s:" x "\n", __FUNCTION__) -+#else -+#define IP6_TNL_TRACE(x...) do {;} while(0) -+#endif -+ -+#define IPV6_TCLASS_MASK (IPV6_FLOWINFO_MASK & ~IPV6_FLOWLABEL_MASK) -+ -+#define HASH_SIZE 32 -+ -+#define HASH(addr) (((addr)->s6_addr32[0] ^ (addr)->s6_addr32[1] ^ \ -+ (addr)->s6_addr32[2] ^ (addr)->s6_addr32[3]) & \ -+ (HASH_SIZE - 1)) -+ -+static int ip6ip6_fb_tnl_dev_init(struct net_device *dev); -+static int ip6ip6_tnl_dev_init(struct net_device *dev); -+ -+/* the IPv6 IPv6 tunnel fallback device */ -+static struct net_device ip6ip6_fb_tnl_dev = { -+ name: "ip6tnl0", -+ init: ip6ip6_fb_tnl_dev_init -+}; -+ -+/* the IPv6 IPv6 fallback tunnel */ -+static struct ip6_tnl ip6ip6_fb_tnl = { -+ dev: &ip6ip6_fb_tnl_dev, -+ parms:{name: "ip6tnl0", proto: IPPROTO_IPV6} -+}; -+ -+/* lists for storing tunnels in use */ -+static struct ip6_tnl *tnls_r_l[HASH_SIZE]; -+static struct ip6_tnl *tnls_wc[1]; -+static struct ip6_tnl **tnls[2] = { tnls_wc, tnls_r_l }; -+ -+/* list for unused cached kernel tunnels */ -+static struct ip6_tnl *tnls_kernel[1]; -+/* maximum number of cached kernel tunnels */ -+static unsigned int max_kdev_count = 0; -+/* minimum number of cached kernel tunnels */ -+static unsigned int min_kdev_count = 0; -+/* current number of cached kernel tunnels */ -+static unsigned int kdev_count = 0; -+ -+/* lists for tunnel hook functions */ -+static struct list_head hooks[IP6_TNL_MAXHOOKS]; -+ -+/* locks for the different lists */ -+static rwlock_t ip6ip6_lock = RW_LOCK_UNLOCKED; -+static rwlock_t ip6ip6_kernel_lock = RW_LOCK_UNLOCKED; -+static rwlock_t ip6ip6_hook_lock = RW_LOCK_UNLOCKED; -+ -+/* flag indicating if the module is being removed */ -+static int shutdown = 0; -+ -+/** -+ * ip6ip6_tnl_lookup - fetch tunnel matching the end-point addresses -+ * @remote: the address of the tunnel exit-point -+ * @local: the address of the tunnel entry-point -+ * -+ * Return: -+ * tunnel matching given end-points if found, -+ * else fallback tunnel if its device is up, -+ * else %NULL -+ **/ -+ -+struct ip6_tnl * -+ip6ip6_tnl_lookup(struct in6_addr *remote, struct in6_addr *local) -+{ -+ unsigned h0 = HASH(remote); -+ unsigned h1 = HASH(local); -+ struct ip6_tnl *t; -+ -+ for (t = tnls_r_l[h0 ^ h1]; t; t = t->next) { -+ if (!ipv6_addr_cmp(local, &t->parms.laddr) && -+ !ipv6_addr_cmp(remote, &t->parms.raddr) && -+ (t->dev->flags & IFF_UP)) -+ return t; -+ } -+ if ((t = tnls_wc[0]) != NULL && (t->dev->flags & IFF_UP)) -+ return t; -+ -+ return NULL; -+} -+ -+/** -+ * ip6ip6_bucket - get head of list matching given tunnel parameters -+ * @p: parameters containing tunnel end-points -+ * -+ * Description: -+ * ip6ip6_bucket() returns the head of the list matching the -+ * &struct in6_addr entries laddr and raddr in @p. -+ * -+ * Return: head of IPv6 tunnel list -+ **/ -+ -+static struct ip6_tnl ** -+ip6ip6_bucket(struct ip6_tnl_parm *p) -+{ -+ struct in6_addr *remote = &p->raddr; -+ struct in6_addr *local = &p->laddr; -+ unsigned h = 0; -+ int prio = 0; -+ -+ if (!ipv6_addr_any(remote) || !ipv6_addr_any(local)) { -+ prio = 1; -+ h = HASH(remote) ^ HASH(local); -+ } -+ return &tnls[prio][h]; -+} -+ -+/** -+ * ip6ip6_kernel_tnl_link - add new kernel tunnel to cache -+ * @t: kernel tunnel -+ * -+ * Note: -+ * %IP6_TNL_F_KERNEL_DEV is assumed to be raised in t->parms.flags. -+ * See the comments on ip6ip6_kernel_tnl_add() for more information. -+ **/ -+ -+static inline void -+ip6ip6_kernel_tnl_link(struct ip6_tnl *t) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ t->next = tnls_kernel[0]; -+ tnls_kernel[0] = t; -+ kdev_count++; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+} -+ -+/** -+ * ip6ip6_kernel_tnl_unlink - remove first kernel tunnel from cache -+ * -+ * Return: first free kernel tunnel -+ * -+ * Note: -+ * See the comments on ip6ip6_kernel_tnl_add() for more information. -+ **/ -+ -+static inline struct ip6_tnl * -+ip6ip6_kernel_tnl_unlink(void) -+{ -+ struct ip6_tnl *t; -+ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ if ((t = tnls_kernel[0]) != NULL) { -+ tnls_kernel[0] = t->next; -+ kdev_count--; -+ } -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ return t; -+} -+ -+/** -+ * ip6ip6_tnl_link - add tunnel to hash table -+ * @t: tunnel to be added -+ **/ -+ -+static void -+ip6ip6_tnl_link(struct ip6_tnl *t) -+{ -+ struct ip6_tnl **tp = ip6ip6_bucket(&t->parms); -+ -+ write_lock_bh(&ip6ip6_lock); -+ t->next = *tp; -+ *tp = t; -+ write_unlock_bh(&ip6ip6_lock); -+} -+ -+/** -+ * ip6ip6_tnl_unlink - remove tunnel from hash table -+ * @t: tunnel to be removed -+ **/ -+ -+static void -+ip6ip6_tnl_unlink(struct ip6_tnl *t) -+{ -+ struct ip6_tnl **tp; -+ -+ write_lock_bh(&ip6ip6_lock); -+ for (tp = ip6ip6_bucket(&t->parms); *tp; tp = &(*tp)->next) { -+ if (t == *tp) { -+ *tp = t->next; -+ break; -+ } -+ } -+ write_unlock_bh(&ip6ip6_lock); -+} -+ -+/** -+ * ip6ip6_tnl_create() - create a new tunnel -+ * @p: tunnel parameters -+ * @pt: pointer to new tunnel -+ * -+ * Description: -+ * Create tunnel matching given parameters. New kernel managed devices are -+ * not put in the normal hash structure, but are instead cached for later -+ * use. -+ * -+ * Return: -+ * 0 on success -+ **/ -+ -+ -+static int __ip6ip6_tnl_create(struct ip6_tnl_parm *p, -+ struct ip6_tnl **pt, -+ int kernel_list) -+{ -+ struct net_device *dev; -+ int err = -ENOBUFS; -+ struct ip6_tnl *t; -+ -+ MOD_INC_USE_COUNT; -+ dev = kmalloc(sizeof (*dev) + sizeof (*t), GFP_KERNEL); -+ if (!dev) { -+ MOD_DEC_USE_COUNT; -+ return err; -+ } -+ memset(dev, 0, sizeof (*dev) + sizeof (*t)); -+ dev->priv = (void *) (dev + 1); -+ t = (struct ip6_tnl *) dev->priv; -+ t->dev = dev; -+ dev->init = ip6ip6_tnl_dev_init; -+ dev->features |= NETIF_F_DYNALLOC; -+ if (kernel_list) { -+ memcpy(t->parms.name, p->name, IFNAMSIZ - 1); -+ t->parms.proto = IPPROTO_IPV6; -+ t->parms.flags = IP6_TNL_F_KERNEL_DEV; -+ } else { -+ memcpy(&t->parms, p, sizeof (*p)); -+ } -+ t->parms.name[IFNAMSIZ - 1] = '\0'; -+ strcpy(dev->name, t->parms.name); -+ if (!dev->name[0]) { -+ int i; -+ for (i = 0; i < IP6_TNL_MAX; i++) { -+ sprintf(dev->name, "ip6tnl%d", i); -+ if (__dev_get_by_name(dev->name) == NULL) -+ break; -+ } -+ -+ if (i == IP6_TNL_MAX) { -+ goto failed; -+ } -+ memcpy(t->parms.name, dev->name, IFNAMSIZ); -+ } -+ if ((err = register_netdevice(dev)) < 0) { -+ goto failed; -+ } -+ dev_hold(dev); -+ if (kernel_list) { -+ ip6ip6_kernel_tnl_link(t); -+ } else { -+ ip6ip6_tnl_link(t); -+ } -+ *pt = t; -+ return 0; -+failed: -+ kfree(dev); -+ MOD_DEC_USE_COUNT; -+ return err; -+} -+ -+ -+int ip6ip6_tnl_create(struct ip6_tnl_parm *p, struct ip6_tnl **pt) -+{ -+ return __ip6ip6_tnl_create(p, pt, 0); -+} -+ -+ -+static void manage_kernel_tnls(void *foo); -+ -+static struct tq_struct manager_task = { -+ routine:manage_kernel_tnls, -+ data:NULL -+}; -+ -+/** -+ * manage_kernel_tnls() - create and destroy kernel tunnels -+ * -+ * Description: -+ * manage_kernel_tnls() creates new kernel devices if there -+ * are less than $min_kdev_count of them and deletes old ones if -+ * there are less than $max_kdev_count of them in the cache -+ * -+ * Note: -+ * Schedules itself to be run later in process context if called from -+ * interrupt. Therefore only works synchronously when called from process -+ * context. -+ **/ -+ -+static void -+manage_kernel_tnls(void *foo) -+{ -+ struct ip6_tnl *t = NULL; -+ struct ip6_tnl_parm parm; -+ -+ /* We can't do this processing in interrupt -+ context so schedule it for later */ -+ if (in_interrupt()) { -+ read_lock(&ip6ip6_kernel_lock); -+ if (!shutdown && -+ (kdev_count < min_kdev_count || -+ kdev_count > max_kdev_count)) { -+ schedule_task(&manager_task); -+ } -+ read_unlock(&ip6ip6_kernel_lock); -+ return; -+ } -+ -+ rtnl_lock(); -+ read_lock_bh(&ip6ip6_kernel_lock); -+ memset(&parm, 0, sizeof (parm)); -+ parm.flags = IP6_TNL_F_KERNEL_DEV; -+ /* Create tunnels until there are at least min_kdev_count */ -+ while (kdev_count < min_kdev_count) { -+ read_unlock_bh(&ip6ip6_kernel_lock); -+ if (!__ip6ip6_tnl_create(&parm, &t, 1)) { -+ dev_open(t->dev); -+ } else { -+ goto err; -+ } -+ read_lock_bh(&ip6ip6_kernel_lock); -+ } -+ -+ /* Destroy tunnels until there are at most max_kdev_count */ -+ while (kdev_count > max_kdev_count) { -+ read_unlock_bh(&ip6ip6_kernel_lock); -+ if ((t = ip6ip6_kernel_tnl_unlink()) != NULL) { -+ unregister_netdevice(t->dev); -+ } else { -+ goto err; -+ } -+ read_lock_bh(&ip6ip6_kernel_lock); -+ } -+ read_unlock_bh(&ip6ip6_kernel_lock); -+err: -+ rtnl_unlock(); -+} -+ -+/** -+ * ip6ip6_tnl_inc_max_kdev_count() - increase max kernel dev cache size -+ * @n: size increase -+ * Description: -+ * Increase the upper limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_inc_max_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ max_kdev_count += n; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return max_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_dec_max_kdev_count() - decrease max kernel dev cache size -+ * @n: size decrement -+ * Description: -+ * Decrease the upper limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_dec_max_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ max_kdev_count -= min(max_kdev_count, n); -+ if (max_kdev_count < min_kdev_count) -+ min_kdev_count = max_kdev_count; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return max_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_inc_min_kdev_count() - increase min kernel dev cache size -+ * @n: size increase -+ * Description: -+ * Increase the lower limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_inc_min_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ min_kdev_count += n; -+ if (min_kdev_count > max_kdev_count) -+ max_kdev_count = min_kdev_count; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return min_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_dec_min_kdev_count() - decrease min kernel dev cache size -+ * @n: size decrement -+ * Description: -+ * Decrease the lower limit for the number of kernel devices allowed in the -+ * cache at any on time. -+ **/ -+ -+unsigned int -+ip6ip6_tnl_dec_min_kdev_count(unsigned int n) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ min_kdev_count -= min(min_kdev_count, n); -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ manage_kernel_tnls(NULL); -+ return min_kdev_count; -+} -+ -+/** -+ * ip6ip6_tnl_locate - find or create tunnel matching given parameters -+ * @p: tunnel parameters -+ * @create: != 0 if allowed to create new tunnel if no match found -+ * -+ * Description: -+ * ip6ip6_tnl_locate() first tries to locate an existing tunnel -+ * based on @parms. If this is unsuccessful, but @create is set a new -+ * tunnel device is created and registered for use. -+ * -+ * Return: -+ * 0 if tunnel located or created, -+ * -EINVAL if parameters incorrect, -+ * -ENODEV if no matching tunnel available -+ **/ -+ -+int ip6ip6_tnl_locate(struct ip6_tnl_parm *p, struct ip6_tnl **pt, int create) -+{ -+ struct in6_addr *remote = &p->raddr; -+ struct in6_addr *local = &p->laddr; -+ struct ip6_tnl *t; -+ -+ if (p->proto != IPPROTO_IPV6) -+ return -EINVAL; -+ -+ for (t = *ip6ip6_bucket(p); t; t = t->next) { -+ if (!ipv6_addr_cmp(local, &t->parms.laddr) && -+ !ipv6_addr_cmp(remote, &t->parms.raddr)) { -+ *pt = t; -+ return (create ? -EEXIST : 0); -+ } -+ } -+ return ip6ip6_tnl_create(p, pt); -+} -+ -+/** -+ * ip6ip6_tnl_dev_destructor - tunnel device destructor -+ * @dev: the device to be destroyed -+ **/ -+ -+static void -+ip6ip6_tnl_dev_destructor(struct net_device *dev) -+{ -+ if (dev != &ip6ip6_fb_tnl_dev) { -+ MOD_DEC_USE_COUNT; -+ } -+} -+ -+/** -+ * ip6ip6_tnl_dev_uninit - tunnel device uninitializer -+ * @dev: the device to be destroyed -+ * -+ * Description: -+ * ip6ip6_tnl_dev_uninit() removes tunnel from its list -+ **/ -+ -+static void -+ip6ip6_tnl_dev_uninit(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ -+ if (dev == &ip6ip6_fb_tnl_dev) { -+ write_lock_bh(&ip6ip6_lock); -+ tnls_wc[0] = NULL; -+ write_unlock_bh(&ip6ip6_lock); -+ } else { -+ ip6ip6_tnl_unlink(t); -+ } -+ sock_release(t->sock); -+ dev_put(dev); -+} -+ -+/** -+ * parse_tvl_tnl_enc_lim - handle encapsulation limit option -+ * @skb: received socket buffer -+ * -+ * Return: -+ * 0 if none was found, -+ * else index to encapsulation limit -+ **/ -+ -+static __u16 -+parse_tlv_tnl_enc_lim(struct sk_buff *skb, __u8 * raw) -+{ -+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) raw; -+ __u8 nexthdr = ipv6h->nexthdr; -+ __u16 off = sizeof (*ipv6h); -+ -+ while (ipv6_ext_hdr(nexthdr) && nexthdr != NEXTHDR_NONE) { -+ __u16 optlen = 0; -+ struct ipv6_opt_hdr *hdr; -+ if (raw + off + sizeof (*hdr) > skb->data && -+ !pskb_may_pull(skb, raw - skb->data + off + sizeof (*hdr))) -+ break; -+ -+ hdr = (struct ipv6_opt_hdr *) (raw + off); -+ if (nexthdr == NEXTHDR_FRAGMENT) { -+ struct frag_hdr *frag_hdr = (struct frag_hdr *) hdr; -+ if (frag_hdr->frag_off) -+ break; -+ optlen = 8; -+ } else if (nexthdr == NEXTHDR_AUTH) { -+ optlen = (hdr->hdrlen + 2) << 2; -+ } else { -+ optlen = ipv6_optlen(hdr); -+ } -+ if (nexthdr == NEXTHDR_DEST) { -+ __u16 i = off + 2; -+ while (1) { -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ -+ /* No more room for encapsulation limit */ -+ if (i + sizeof (*tel) > off + optlen) -+ break; -+ -+ tel = (struct ipv6_tlv_tnl_enc_lim *) &raw[i]; -+ /* return index of option if found and valid */ -+ if (tel->type == IPV6_TLV_TNL_ENCAP_LIMIT && -+ tel->length == 1) -+ return i; -+ /* else jump to next option */ -+ if (tel->type) -+ i += tel->length + 2; -+ else -+ i++; -+ } -+ } -+ nexthdr = hdr->nexthdr; -+ off += optlen; -+ } -+ return 0; -+} -+ -+/** -+ * ip6ip6_err - tunnel error handler -+ * -+ * Description: -+ * ip6ip6_err() should handle errors in the tunnel according -+ * to the specifications in RFC 2473. -+ **/ -+ -+void ip6ip6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, -+ int type, int code, int offset, __u32 info) -+{ -+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) skb->data; -+ struct ip6_tnl *t; -+ int rel_msg = 0; -+ int rel_type = ICMPV6_DEST_UNREACH; -+ int rel_code = ICMPV6_ADDR_UNREACH; -+ __u32 rel_info = 0; -+ __u16 len; -+ -+ /* If the packet doesn't contain the original IPv6 header we are -+ in trouble since we might need the source address for furter -+ processing of the error. */ -+ -+ read_lock(&ip6ip6_lock); -+ if ((t = ip6ip6_tnl_lookup(&ipv6h->daddr, &ipv6h->saddr)) == NULL) -+ goto out; -+ -+ switch (type) { -+ __u32 teli; -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ __u32 mtu; -+ case ICMPV6_DEST_UNREACH: -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Path to destination invalid " -+ "or inactive!\n", t->parms.name); -+ rel_msg = 1; -+ break; -+ case ICMPV6_TIME_EXCEED: -+ if (code == ICMPV6_EXC_HOPLIMIT) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Too small hop limit or " -+ "routing loop in tunnel!\n", -+ t->parms.name); -+ rel_msg = 1; -+ } -+ break; -+ case ICMPV6_PARAMPROB: -+ /* ignore if parameter problem not caused by a tunnel -+ encapsulation limit sub-option */ -+ if (code != ICMPV6_HDR_FIELD) { -+ break; -+ } -+ teli = parse_tlv_tnl_enc_lim(skb, skb->data); -+ -+ if (teli && teli == ntohl(info) - 2) { -+ tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->data[teli]; -+ if (tel->encap_limit == 0) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Too small encapsulation " -+ "limit or routing loop in " -+ "tunnel!\n", t->parms.name); -+ rel_msg = 1; -+ } -+ } -+ break; -+ case ICMPV6_PKT_TOOBIG: -+ mtu = ntohl(info) - offset; -+ if (mtu < IPV6_MIN_MTU) -+ mtu = IPV6_MIN_MTU; -+ t->dev->mtu = mtu; -+ -+ if ((len = sizeof (*ipv6h) + ipv6h->payload_len) > mtu) { -+ rel_type = ICMPV6_PKT_TOOBIG; -+ rel_code = 0; -+ rel_info = mtu; -+ rel_msg = 1; -+ } -+ break; -+ } -+ if (rel_msg && pskb_may_pull(skb, offset + sizeof (*ipv6h))) { -+ struct rt6_info *rt; -+ struct sk_buff *skb2 = skb_clone(skb, GFP_ATOMIC); -+ if (!skb2) -+ goto out; -+ -+ dst_release(skb2->dst); -+ skb2->dst = NULL; -+ skb_pull(skb2, offset); -+ skb2->nh.raw = skb2->data; -+ -+ /* Try to guess incoming interface */ -+ rt = rt6_lookup(&skb2->nh.ipv6h->saddr, NULL, 0, 0); -+ -+ if (rt && rt->rt6i_dev) -+ skb2->dev = rt->rt6i_dev; -+ -+ icmpv6_send(skb2, rel_type, rel_code, rel_info, skb2->dev); -+ -+ if (rt) -+ dst_release(&rt->u.dst); -+ -+ kfree_skb(skb2); -+ } -+out: -+ read_unlock(&ip6ip6_lock); -+} -+ -+/** -+ * call_hooks - call ipv6 tunnel hooks -+ * @hooknum: hook number, either %IP6_TNL_PRE_ENCAP, or -+ * %IP6_TNL_PRE_DECAP -+ * @t: the current tunnel -+ * @skb: the tunneled packet -+ * -+ * Description: -+ * Pass packet to all the hook functions until %IP6_TNL_DROP -+ * -+ * Return: -+ * %IP6_TNL_ACCEPT or %IP6_TNL_DROP -+ **/ -+ -+static inline int -+call_hooks(unsigned int hooknum, struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ struct ip6_tnl_hook_ops *h; -+ int accept = IP6_TNL_ACCEPT; -+ -+ if (hooknum < IP6_TNL_MAXHOOKS) { -+ struct list_head *i; -+ read_lock(&ip6ip6_hook_lock); -+ for (i = hooks[hooknum].next; i != &hooks[hooknum]; i = i->next) { -+ h = (struct ip6_tnl_hook_ops *) i; -+ -+ if (h->hook) { -+ accept = h->hook(t, skb); -+ -+ if (accept != IP6_TNL_ACCEPT) -+ break; -+ } -+ } -+ read_unlock(&ip6ip6_hook_lock); -+ } -+ return accept; -+} -+ -+/** -+ * ip6ip6_rcv - decapsulate IPv6 packet and retransmit it locally -+ * @skb: received socket buffer -+ * -+ * Return: 0 -+ **/ -+ -+int ip6ip6_rcv(struct sk_buff *skb) -+{ -+ struct ipv6hdr *ipv6h; -+ struct ip6_tnl *t; -+ -+ if (!pskb_may_pull(skb, sizeof (*ipv6h))) -+ goto discard; -+ -+ ipv6h = skb->nh.ipv6h; -+ -+ read_lock(&ip6ip6_lock); -+ -+ if ((t = ip6ip6_tnl_lookup(&ipv6h->saddr, &ipv6h->daddr)) != NULL) { -+ if (!(t->parms.flags & IP6_TNL_F_CAP_RCV) || -+ call_hooks(IP6_TNL_PRE_DECAP, t, skb) != IP6_TNL_ACCEPT) { -+ t->stat.rx_dropped++; -+ read_unlock(&ip6ip6_lock); -+ goto discard; -+ } -+ skb->mac.raw = skb->nh.raw; -+ skb->nh.raw = skb->data; -+ skb->protocol = htons(ETH_P_IPV6); -+ skb->pkt_type = PACKET_HOST; -+ memset(skb->cb, 0, sizeof(struct inet6_skb_parm)); -+ skb->dev = t->dev; -+ dst_release(skb->dst); -+ skb->dst = NULL; -+ t->stat.rx_packets++; -+ t->stat.rx_bytes += skb->len; -+ netif_rx(skb); -+ read_unlock(&ip6ip6_lock); -+ return 0; -+ } -+ read_unlock(&ip6ip6_lock); -+ icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); -+discard: -+ kfree_skb(skb); -+ return 0; -+} -+ -+static inline struct ipv6_txoptions *create_tel(__u8 encap_limit) -+{ -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ struct ipv6_txoptions *opt; -+ __u8 *raw; -+ -+ int opt_len = sizeof(*opt) + IPV6_TLV_TEL_DST_SIZE; -+ -+ if (!(opt = kmalloc(opt_len, GFP_ATOMIC))) { -+ return NULL; -+ } -+ memset(opt, 0, opt_len); -+ opt->tot_len = opt_len; -+ opt->dst0opt = (struct ipv6_opt_hdr *) (opt + 1); -+ opt->opt_nflen = 8; -+ -+ tel = (struct ipv6_tlv_tnl_enc_lim *) (opt->dst0opt + 1); -+ tel->type = IPV6_TLV_TNL_ENCAP_LIMIT; -+ tel->length = 1; -+ tel->encap_limit = encap_limit; -+ -+ raw = (__u8 *) opt->dst0opt; -+ raw[5] = IPV6_TLV_PADN; -+ raw[6] = 1; -+ -+ return opt; -+} -+ -+static int -+ip6ip6_getfrag(const void *data, struct in6_addr *addr, -+ char *buff, unsigned int offset, unsigned int len) -+{ -+ memcpy(buff, data + offset, len); -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_addr_conflict - compare packet addresses to tunnel's own -+ * @t: the outgoing tunnel device -+ * @hdr: IPv6 header from the incoming packet -+ * -+ * Description: -+ * Avoid trivial tunneling loop by checking that tunnel exit-point -+ * doesn't match source of incoming packet. -+ * -+ * Return: -+ * 1 if conflict, -+ * 0 else -+ **/ -+ -+static inline int -+ip6ip6_tnl_addr_conflict(struct ip6_tnl *t, struct ipv6hdr *hdr) -+{ -+ return !ipv6_addr_cmp(&t->parms.raddr, &hdr->saddr); -+} -+ -+/** -+ * ip6ip6_tnl_xmit - encapsulate packet and send -+ * @skb: the outgoing socket buffer -+ * @dev: the outgoing tunnel device -+ * -+ * Description: -+ * Build new header and do some sanity checks on the packet before sending -+ * it to ip6_build_xmit(). -+ * -+ * Return: -+ * 0 -+ **/ -+ -+int ip6ip6_tnl_xmit(struct sk_buff *skb, struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ struct net_device_stats *stats = &t->stat; -+ struct ipv6hdr *ipv6h = skb->nh.ipv6h; -+ struct ipv6_txoptions *opt = NULL; -+ int encap_limit = -1; -+ __u16 offset; -+ struct flowi fl; -+ int err = 0; -+ struct dst_entry *dst; -+ struct sock *sk = t->sock->sk; -+ struct ipv6_pinfo *np = &sk->net_pinfo.af_inet6; -+ int mtu; -+ -+ if (t->recursion++) { -+ stats->collisions++; -+ goto tx_err; -+ } -+ if (skb->protocol != htons(ETH_P_IPV6) || -+ !(t->parms.flags & IP6_TNL_F_CAP_XMIT) || -+ ip6ip6_tnl_addr_conflict(t, ipv6h)) { -+ goto tx_err; -+ } -+ if ((offset = parse_tlv_tnl_enc_lim(skb, skb->nh.raw)) > 0) { -+ struct ipv6_tlv_tnl_enc_lim *tel; -+ tel = (struct ipv6_tlv_tnl_enc_lim *) &skb->nh.raw[offset]; -+ if (tel->encap_limit == 0) { -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, offset + 2, skb->dev); -+ goto tx_err; -+ } -+ encap_limit = tel->encap_limit - 1; -+ } else if (!(t->parms.flags & IP6_TNL_F_IGN_ENCAP_LIMIT)) { -+ encap_limit = t->parms.encap_limit; -+ } -+ if (call_hooks(IP6_TNL_PRE_ENCAP, t, skb) != IP6_TNL_ACCEPT) -+ goto discard; -+ memcpy(&fl, &t->fl, sizeof (fl)); -+ -+ if ((t->parms.flags & IP6_TNL_F_USE_ORIG_TCLASS)) -+ fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_TCLASS_MASK); -+ if ((t->parms.flags & IP6_TNL_F_USE_ORIG_FLOWLABEL)) -+ fl.fl6_flowlabel |= (*(__u32 *) ipv6h & IPV6_FLOWLABEL_MASK); -+ -+ if (encap_limit >= 0 && (opt = create_tel(encap_limit)) == NULL) -+ goto tx_err; -+ -+ dst = __sk_dst_check(sk, np->dst_cookie); -+ -+ if (dst) { -+ if (np->daddr_cache == NULL || -+ ipv6_addr_cmp(fl.fl6_dst, np->daddr_cache) || -+#ifdef CONFIG_IPV6_SUBTREES -+ np->saddr_cache == NULL || -+ ipv6_addr_cmp(fl.fl6_src, np->saddr_cache) || -+#endif -+ (fl.oif && fl.oif != dst->dev->ifindex)) { -+ dst = NULL; -+ } else { -+ dst_hold(dst); -+ } -+ } -+ if (dst == NULL) { -+ dst = ip6_route_output(sk, &fl); -+ if (dst->error) { -+ stats->tx_carrier_errors++; -+ dst_link_failure(skb); -+ goto tx_err_dst_release; -+ } -+ /* local routing loop */ -+ if (dst->dev == dev) { -+ stats->collisions++; -+ if (net_ratelimit()) -+ printk(KERN_WARNING -+ "%s: Local routing loop detected!\n", -+ t->parms.name); -+ goto tx_err_dst_release; -+ } -+ } -+ mtu = dst->pmtu - sizeof (*ipv6h); -+ if (opt) { -+ mtu -= (opt->opt_nflen + opt->opt_flen); -+ } -+ if (mtu < IPV6_MIN_MTU) -+ mtu = IPV6_MIN_MTU; -+ if (skb->dst && mtu < skb->dst->pmtu) { -+ struct rt6_info *rt = (struct rt6_info *) skb->dst; -+ rt->rt6i_flags |= RTF_MODIFIED; -+ rt->u.dst.pmtu = mtu; -+ } -+ if (skb->len > mtu) { -+ icmpv6_send(skb, ICMPV6_PKT_TOOBIG, 0, mtu, dev); -+ goto tx_err_dst_release; -+ } -+ ip6_dst_store(sk, dst, &np->daddr, &np->saddr); -+ err = ip6_build_xmit(sk, ip6ip6_getfrag, (void *) skb->nh.raw, -+ &fl, skb->len, opt, t->parms.hop_limit, -+ MSG_DONTWAIT); -+ -+ if (err == NET_XMIT_SUCCESS || err == NET_XMIT_CN) { -+ stats->tx_bytes += skb->len; -+ stats->tx_packets++; -+ } else { -+ stats->tx_errors++; -+ stats->tx_aborted_errors++; -+ } -+ if (opt) -+ kfree(opt); -+ kfree_skb(skb); -+ t->recursion--; -+ return 0; -+tx_err_dst_release: -+ dst_release(dst); -+ if (opt) -+ kfree(opt); -+tx_err: -+ stats->tx_errors++; -+discard: -+ stats->tx_dropped++; -+ kfree_skb(skb); -+ t->recursion--; -+ return 0; -+} -+ -+static void ip6_tnl_set_cap(struct ip6_tnl *t) -+{ -+ struct ip6_tnl_parm *p = &t->parms; -+ struct in6_addr *laddr = &p->laddr; -+ struct in6_addr *raddr = &p->raddr; -+ int ltype = ipv6_addr_type(laddr); -+ int rtype = ipv6_addr_type(raddr); -+ -+ p->flags &= ~(IP6_TNL_F_CAP_XMIT|IP6_TNL_F_CAP_RCV); -+ -+ if (ltype != IPV6_ADDR_ANY && rtype != IPV6_ADDR_ANY && -+ ((ltype|rtype) & -+ (IPV6_ADDR_UNICAST| -+ IPV6_ADDR_LOOPBACK|IPV6_ADDR_LINKLOCAL| -+ IPV6_ADDR_MAPPED|IPV6_ADDR_RESERVED)) == IPV6_ADDR_UNICAST) { -+ struct net_device *ldev = NULL; -+ int l_ok = 1; -+ int r_ok = 1; -+ -+ if (p->link) -+ ldev = dev_get_by_index(p->link); -+ -+ if ((ltype&IPV6_ADDR_UNICAST) && !ipv6_chk_addr(laddr, ldev)) -+ l_ok = 0; -+ -+ if ((rtype&IPV6_ADDR_UNICAST) && ipv6_chk_addr(raddr, NULL)) -+ r_ok = 0; -+ -+ if (l_ok && r_ok) { -+ if (ltype&IPV6_ADDR_UNICAST) -+ p->flags |= IP6_TNL_F_CAP_XMIT; -+ if (rtype&IPV6_ADDR_UNICAST) -+ p->flags |= IP6_TNL_F_CAP_RCV; -+ } -+ if (ldev) -+ dev_put(ldev); -+ } -+} -+ -+static void ip6ip6_tnl_link_config(struct ip6_tnl *t) -+{ -+ struct net_device *dev = t->dev; -+ struct ip6_tnl_parm *p = &t->parms; -+ struct flowi *fl = &t->fl; -+ -+ /* Set up flowi template */ -+ fl->fl6_src = &p->laddr; -+ fl->fl6_dst = &p->raddr; -+ fl->oif = p->link; -+ fl->fl6_flowlabel = 0; -+ -+ if (!(p->flags&IP6_TNL_F_USE_ORIG_TCLASS)) -+ fl->fl6_flowlabel |= IPV6_TCLASS_MASK & htonl(p->flowinfo); -+ if (!(p->flags&IP6_TNL_F_USE_ORIG_FLOWLABEL)) -+ fl->fl6_flowlabel |= IPV6_FLOWLABEL_MASK & htonl(p->flowinfo); -+ -+ ip6_tnl_set_cap(t); -+ -+ if (p->flags&IP6_TNL_F_CAP_XMIT && p->flags&IP6_TNL_F_CAP_RCV) -+ dev->flags |= IFF_POINTOPOINT; -+ else -+ dev->flags &= ~IFF_POINTOPOINT; -+ -+ if (p->flags & IP6_TNL_F_CAP_XMIT) { -+ struct rt6_info *rt = rt6_lookup(&p->raddr, &p->laddr, -+ p->link, 0); -+ -+ if (rt == NULL) -+ return; -+ -+ if (rt->rt6i_dev) { -+ dev->iflink = rt->rt6i_dev->ifindex; -+ -+ dev->hard_header_len = rt->rt6i_dev->hard_header_len + -+ sizeof (struct ipv6hdr); -+ -+ dev->mtu = rt->rt6i_dev->mtu - sizeof (struct ipv6hdr); -+ -+ if (dev->mtu < IPV6_MIN_MTU) -+ dev->mtu = IPV6_MIN_MTU; -+ } -+ dst_release(&rt->u.dst); -+ } -+} -+ -+/** -+ * __ip6ip6_tnl_change - update the tunnel parameters -+ * @t: tunnel to be changed -+ * @p: tunnel configuration parameters -+ * -+ * Description: -+ * __ip6ip6_tnl_change() updates the tunnel parameters -+ **/ -+ -+static void -+__ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) -+{ -+ ipv6_addr_copy(&t->parms.laddr, &p->laddr); -+ ipv6_addr_copy(&t->parms.raddr, &p->raddr); -+ t->parms.flags = p->flags; -+ t->parms.hop_limit = p->hop_limit; -+ t->parms.encap_limit = p->encap_limit; -+ t->parms.flowinfo = p->flowinfo; -+ ip6ip6_tnl_link_config(t); -+} -+ -+void ip6ip6_tnl_change(struct ip6_tnl *t, struct ip6_tnl_parm *p) -+{ -+ ip6ip6_tnl_unlink(t); -+ __ip6ip6_tnl_change(t, p); -+ ip6ip6_tnl_link(t); -+} -+ -+/** -+ * ip6ip6_kernel_tnl_add - configure and add kernel tunnel to hash -+ * @p: kernel tunnel configuration parameters -+ * -+ * Description: -+ * ip6ip6_kernel_tnl_add() fetches an unused kernel tunnel configures -+ * it according to @p and places it among the active tunnels. -+ * -+ * Return: -+ * number of references to tunnel on success, -+ * %-EEXIST if there is already a device matching description -+ * %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised, -+ * %-ENODEV if there are no unused kernel tunnels available -+ * -+ * Note: -+ * The code for creating, opening, closing and destroying network devices -+ * must be called from process context, while the Mobile IP code, which -+ * needs the tunnel devices, unfortunately runs in interrupt context. -+ * -+ * The devices must be created and opened in advance, then placed in a -+ * list where the kernel can fetch and ready them for use at a later time. -+ * -+ **/ -+ -+int -+ip6ip6_kernel_tnl_add(struct ip6_tnl_parm *p) -+{ -+ struct ip6_tnl *t; -+ -+ if (!(p->flags & IP6_TNL_F_KERNEL_DEV)) -+ return -EINVAL; -+ if ((t = ip6ip6_tnl_lookup(&p->raddr, &p->laddr)) != NULL && -+ t != &ip6ip6_fb_tnl) { -+ /* Handle duplicate tunnels by incrementing -+ reference count */ -+ atomic_inc(&t->refcnt); -+ goto out; -+ } -+ if ((t = ip6ip6_kernel_tnl_unlink()) == NULL) -+ return -ENODEV; -+ __ip6ip6_tnl_change(t, p); -+ -+ atomic_inc(&t->refcnt); -+ -+ ip6ip6_tnl_link(t); -+ -+ manage_kernel_tnls(NULL); -+out: -+ return atomic_read(&t->refcnt); -+} -+ -+/** -+ * ip6ip6_kernel_tnl_del - delete no longer needed kernel tunnel -+ * @t: kernel tunnel to be removed from hash -+ * -+ * Description: -+ * ip6ip6_kernel_tnl_del() removes and deconfigures the tunnel @t -+ * and places it among the unused kernel devices. -+ * -+ * Return: -+ * number of references on success, -+ * %-EINVAL if p->flags doesn't have %IP6_TNL_F_KERNEL_DEV raised, -+ * -+ * Note: -+ * See the comments on ip6ip6_kernel_tnl_add() for more information. -+ **/ -+ -+int -+ip6ip6_kernel_tnl_del(struct ip6_tnl *t) -+{ -+ if (!t) -+ return -ENODEV; -+ -+ if (!(t->parms.flags & IP6_TNL_F_KERNEL_DEV)) -+ return -EINVAL; -+ -+ if (atomic_dec_and_test(&t->refcnt)) { -+ struct ip6_tnl_parm p; -+ ip6ip6_tnl_unlink(t); -+ memset(&p, 0, sizeof (p)); -+ p.flags = IP6_TNL_F_KERNEL_DEV; -+ -+ __ip6ip6_tnl_change(t, &p); -+ -+ ip6ip6_kernel_tnl_link(t); -+ -+ manage_kernel_tnls(NULL); -+ } -+ return atomic_read(&t->refcnt); -+} -+ -+/** -+ * ip6ip6_tnl_ioctl - configure ipv6 tunnels from userspace -+ * @dev: virtual device associated with tunnel -+ * @ifr: parameters passed from userspace -+ * @cmd: command to be performed -+ * -+ * Description: -+ * ip6ip6_tnl_ioctl() is used for managing IPv6 tunnels -+ * from userspace. -+ * -+ * The possible commands are the following: -+ * %SIOCGETTUNNEL: get tunnel parameters for device -+ * %SIOCADDTUNNEL: add tunnel matching given tunnel parameters -+ * %SIOCCHGTUNNEL: change tunnel parameters to those given -+ * %SIOCDELTUNNEL: delete tunnel -+ * -+ * The fallback device "ip6tnl0", created during module -+ * initialization, can be used for creating other tunnel devices. -+ * -+ * Return: -+ * 0 on success, -+ * %-EFAULT if unable to copy data to or from userspace, -+ * %-EPERM if current process hasn't %CAP_NET_ADMIN set or attempting -+ * to configure kernel devices from userspace, -+ * %-EINVAL if passed tunnel parameters are invalid, -+ * %-EEXIST if changing a tunnel's parameters would cause a conflict -+ * %-ENODEV if attempting to change or delete a nonexisting device -+ * -+ * Note: -+ * See the comments on ip6ip6_kernel_tnl_add() for more information -+ * about kernel tunnels. -+ * **/ -+ -+static int -+ip6ip6_tnl_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) -+{ -+ int err = 0; -+ int create; -+ struct ip6_tnl_parm p; -+ struct ip6_tnl *t = NULL; -+ -+ MOD_INC_USE_COUNT; -+ -+ switch (cmd) { -+ case SIOCGETTUNNEL: -+ if (dev == &ip6ip6_fb_tnl_dev) { -+ if (copy_from_user(&p, -+ ifr->ifr_ifru.ifru_data, -+ sizeof (p))) { -+ err = -EFAULT; -+ break; -+ } -+ if ((err = ip6ip6_tnl_locate(&p, &t, 0)) == -ENODEV) -+ t = (struct ip6_tnl *) dev->priv; -+ else if (err) -+ break; -+ } else -+ t = (struct ip6_tnl *) dev->priv; -+ -+ memcpy(&p, &t->parms, sizeof (p)); -+ if (copy_to_user(ifr->ifr_ifru.ifru_data, &p, sizeof (p))) { -+ err = -EFAULT; -+ } -+ break; -+ case SIOCADDTUNNEL: -+ case SIOCCHGTUNNEL: -+ err = -EPERM; -+ create = (cmd == SIOCADDTUNNEL); -+ if (!capable(CAP_NET_ADMIN)) -+ break; -+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, sizeof (p))) { -+ err = -EFAULT; -+ break; -+ } -+ if (p.flags & IP6_TNL_F_KERNEL_DEV) { -+ break; -+ } -+ if (!create && dev != &ip6ip6_fb_tnl_dev) { -+ t = (struct ip6_tnl *) dev->priv; -+ } -+ if (!t && (err = ip6ip6_tnl_locate(&p, &t, create))) { -+ break; -+ } -+ if (cmd == SIOCCHGTUNNEL) { -+ if (t->dev != dev) { -+ err = -EEXIST; -+ break; -+ } -+ if (t->parms.flags & IP6_TNL_F_KERNEL_DEV) { -+ err = -EPERM; -+ break; -+ } -+ ip6ip6_tnl_change(t, &p); -+ netdev_state_change(dev); -+ } -+ if (copy_to_user(ifr->ifr_ifru.ifru_data, -+ &t->parms, sizeof (p))) { -+ err = -EFAULT; -+ } else { -+ err = 0; -+ } -+ break; -+ case SIOCDELTUNNEL: -+ err = -EPERM; -+ if (!capable(CAP_NET_ADMIN)) -+ break; -+ -+ if (dev == &ip6ip6_fb_tnl_dev) { -+ if (copy_from_user(&p, ifr->ifr_ifru.ifru_data, -+ sizeof (p))) { -+ err = -EFAULT; -+ break; -+ } -+ err = ip6ip6_tnl_locate(&p, &t, 0); -+ if (err) -+ break; -+ if (t == &ip6ip6_fb_tnl) { -+ err = -EPERM; -+ break; -+ } -+ } else { -+ t = (struct ip6_tnl *) dev->priv; -+ } -+ if (t->parms.flags & IP6_TNL_F_KERNEL_DEV) -+ err = -EPERM; -+ else -+ err = unregister_netdevice(t->dev); -+ break; -+ default: -+ err = -EINVAL; -+ } -+ MOD_DEC_USE_COUNT; -+ return err; -+} -+ -+/** -+ * ip6ip6_tnl_get_stats - return the stats for tunnel device -+ * @dev: virtual device associated with tunnel -+ * -+ * Return: stats for device -+ **/ -+ -+static struct net_device_stats * -+ip6ip6_tnl_get_stats(struct net_device *dev) -+{ -+ return &(((struct ip6_tnl *) dev->priv)->stat); -+} -+ -+/** -+ * ip6ip6_tnl_change_mtu - change mtu manually for tunnel device -+ * @dev: virtual device associated with tunnel -+ * @new_mtu: the new mtu -+ * -+ * Return: -+ * 0 on success, -+ * %-EINVAL if mtu too small -+ **/ -+ -+static int -+ip6ip6_tnl_change_mtu(struct net_device *dev, int new_mtu) -+{ -+ if (new_mtu < IPV6_MIN_MTU) { -+ return -EINVAL; -+ } -+ dev->mtu = new_mtu; -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_dev_init_gen - general initializer for all tunnel devices -+ * @dev: virtual device associated with tunnel -+ * -+ * Description: -+ * Set function pointers and initialize the &struct flowi template used -+ * by the tunnel. -+ **/ -+ -+static int -+ip6ip6_tnl_dev_init_gen(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ struct flowi *fl = &t->fl; -+ int err; -+ struct sock *sk; -+ -+ if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_IPV6, &t->sock))) { -+ printk(KERN_ERR -+ "Failed to create IPv6 tunnel socket (err %d).\n", err); -+ return err; -+ } -+ t->sock->inode->i_uid = 0; -+ t->sock->inode->i_gid = 0; -+ -+ sk = t->sock->sk; -+ sk->allocation = GFP_ATOMIC; -+ sk->net_pinfo.af_inet6.hop_limit = 254; -+ sk->net_pinfo.af_inet6.mc_loop = 0; -+ sk->prot->unhash(sk); -+ -+ memset(fl, 0, sizeof (*fl)); -+ fl->proto = IPPROTO_IPV6; -+ -+ dev->destructor = ip6ip6_tnl_dev_destructor; -+ dev->uninit = ip6ip6_tnl_dev_uninit; -+ dev->hard_start_xmit = ip6ip6_tnl_xmit; -+ dev->get_stats = ip6ip6_tnl_get_stats; -+ dev->do_ioctl = ip6ip6_tnl_ioctl; -+ dev->change_mtu = ip6ip6_tnl_change_mtu; -+ -+ dev->type = ARPHRD_TUNNEL6; -+ dev->hard_header_len = LL_MAX_HEADER + sizeof (struct ipv6hdr); -+ dev->mtu = ETH_DATA_LEN - sizeof (struct ipv6hdr); -+ dev->flags |= IFF_NOARP; -+ dev->iflink = 0; -+ /* Hmm... MAX_ADDR_LEN is 8, so the ipv6 addresses can't be -+ copied to dev->dev_addr and dev->broadcast, like the ipv4 -+ addresses were in ipip.c, ip_gre.c and sit.c. */ -+ dev->addr_len = 0; -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_dev_init - initializer for all non fallback tunnel devices -+ * @dev: virtual device associated with tunnel -+ **/ -+ -+static int -+ip6ip6_tnl_dev_init(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *) dev->priv; -+ ip6ip6_tnl_dev_init_gen(dev); -+ ip6ip6_tnl_link_config(t); -+ return 0; -+} -+ -+#ifdef MODULE -+ -+/** -+ * ip6ip6_fb_tnl_open - function called when fallback device opened -+ * @dev: fallback device -+ * -+ * Return: 0 -+ **/ -+ -+static int -+ip6ip6_fb_tnl_open(struct net_device *dev) -+{ -+ MOD_INC_USE_COUNT; -+ return 0; -+} -+ -+/** -+ * ip6ip6_fb_tnl_close - function called when fallback device closed -+ * @dev: fallback device -+ * -+ * Return: 0 -+ **/ -+ -+static int -+ip6ip6_fb_tnl_close(struct net_device *dev) -+{ -+ MOD_DEC_USE_COUNT; -+ return 0; -+} -+#endif -+ -+/** -+ * ip6ip6_fb_tnl_dev_init - initializer for fallback tunnel device -+ * @dev: fallback device -+ * -+ * Return: 0 -+ **/ -+ -+int __init -+ip6ip6_fb_tnl_dev_init(struct net_device *dev) -+{ -+ ip6ip6_tnl_dev_init_gen(dev); -+#ifdef MODULE -+ dev->open = ip6ip6_fb_tnl_open; -+ dev->stop = ip6ip6_fb_tnl_close; -+#endif -+ dev_hold(dev); -+ tnls_wc[0] = &ip6ip6_fb_tnl; -+ return 0; -+} -+ -+/** -+ * ip6ip6_tnl_register_hook - add hook for processing of tunneled packets -+ * @reg: hook function and its parameters -+ * -+ * Description: -+ * Add a netfilter like hook function for special handling of tunneled -+ * packets. The hook functions are called before encapsulation -+ * (%IP6_TNL_PRE_ENCAP) and before decapsulation -+ * (%IP6_TNL_PRE_DECAP). The possible return values by the hook -+ * functions are %IP6_TNL_DROP, %IP6_TNL_ACCEPT and -+ * %IP6_TNL_STOLEN (in case the hook function took care of the packet -+ * and it doesn't have to be processed any further). -+ **/ -+ -+void -+ip6ip6_tnl_register_hook(struct ip6_tnl_hook_ops *reg) -+{ -+ if (reg->hooknum < IP6_TNL_MAXHOOKS) { -+ struct list_head *i; -+ -+ write_lock_bh(&ip6ip6_hook_lock); -+ for (i = hooks[reg->hooknum].next; -+ i != &hooks[reg->hooknum]; i = i->next) { -+ if (reg->priority < -+ ((struct ip6_tnl_hook_ops *) i)->priority) { -+ break; -+ } -+ } -+ list_add(®->list, i->prev); -+ write_unlock_bh(&ip6ip6_hook_lock); -+ } -+} -+ -+/** -+ * ip6ip6_tnl_unregister_hook - remove tunnel hook -+ * @reg: hook function and its parameters -+ **/ -+ -+void -+ip6ip6_tnl_unregister_hook(struct ip6_tnl_hook_ops *reg) -+{ -+ if (reg->hooknum < IP6_TNL_MAXHOOKS) { -+ write_lock_bh(&ip6ip6_hook_lock); -+ list_del(®->list); -+ write_unlock_bh(&ip6ip6_hook_lock); -+ } -+} -+ -+ -+/* the IPv6 over IPv6 protocol structure */ -+static struct inet6_protocol ip6ip6_protocol = { -+ ip6ip6_rcv, /* IPv6 handler */ -+ ip6ip6_err, /* IPv6 error control */ -+ NULL, /* next */ -+ IPPROTO_IPV6, /* protocol ID */ -+ 0, /* copy */ -+ NULL, /* data */ -+ "IPv6 over IPv6" /* name */ -+}; -+ -+/** -+ * ip6_tunnel_init - register protocol and reserve needed resources -+ * -+ * Return: 0 on success -+ **/ -+ -+int __init ip6_tunnel_init(void) -+{ -+ int i, err; -+ -+ ip6ip6_fb_tnl_dev.priv = (void *) &ip6ip6_fb_tnl; -+ -+ for (i = 0; i < IP6_TNL_MAXHOOKS; i++) { -+ INIT_LIST_HEAD(&hooks[i]); -+ } -+ if ((err = register_netdev(&ip6ip6_fb_tnl_dev))) -+ return err; -+ -+ inet6_add_protocol(&ip6ip6_protocol); -+ return 0; -+} -+ -+/** -+ * ip6_tunnel_cleanup - free resources and unregister protocol -+ **/ -+ -+void ip6_tunnel_cleanup(void) -+{ -+ write_lock_bh(&ip6ip6_kernel_lock); -+ shutdown = 1; -+ write_unlock_bh(&ip6ip6_kernel_lock); -+ flush_scheduled_tasks(); -+ manage_kernel_tnls(NULL); -+ inet6_del_protocol(&ip6ip6_protocol); -+ unregister_netdev(&ip6ip6_fb_tnl_dev); -+} -+ -+#ifdef MODULE -+module_init(ip6_tunnel_init); -+module_exit(ip6_tunnel_cleanup); -+#endif -+ -+#if defined(CONFIG_IPV6_MOBILITY_HA_MODULE) || defined(CONFIG_IPV6_MOBILITY_MN_MODULE) -+EXPORT_SYMBOL(ip6ip6_tnl_register_hook); -+EXPORT_SYMBOL(ip6ip6_tnl_unregister_hook); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_HA_MODULE -+EXPORT_SYMBOL(ip6ip6_tnl_dec_max_kdev_count); -+EXPORT_SYMBOL(ip6ip6_tnl_inc_max_kdev_count); -+EXPORT_SYMBOL(ip6ip6_tnl_dec_min_kdev_count); -+EXPORT_SYMBOL(ip6ip6_tnl_inc_min_kdev_count); -+EXPORT_SYMBOL(ip6ip6_kernel_tnl_add); -+EXPORT_SYMBOL(ip6ip6_kernel_tnl_del); -+EXPORT_SYMBOL(ip6ip6_tnl_lookup); -+#endif -+#ifdef CONFIG_IPV6_MOBILITY_MN_MODULE -+EXPORT_SYMBOL(ip6ip6_tnl_create); -+EXPORT_SYMBOL(ip6ip6_tnl_change); -+#endif -+ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mipglue.c -@@ -0,0 +1,63 @@ -+/* -+ * Glue for Mobility support integration to IPv6 -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/sched.h> -+ -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/neighbour.h> -+#include <net/mipglue.h> -+ -+extern int ip6_tlvopt_unknown(struct sk_buff *skb, int optoff); -+ -+/* Initialize all zero */ -+struct mipv6_callable_functions mipv6_functions = { NULL }; -+ -+/* Sets mipv6_functions struct to zero to invalidate all successive -+ * calls to mipv6 functions. Used on module unload. */ -+ -+void mipv6_invalidate_calls(void) -+{ -+ memset(&mipv6_functions, 0, sizeof(mipv6_functions)); -+} -+ -+ -+/* Selects correct handler for tlv encoded destination option. Called -+ * by ip6_parse_tlv. Checks if mipv6 calls are valid before calling. */ -+ -+int mipv6_handle_dstopt(struct sk_buff *skb, int optoff) -+{ -+ int ret; -+ -+ switch (skb->nh.raw[optoff]) { -+ case MIPV6_TLV_HOMEADDR: -+ ret = MIPV6_CALLFUNC(mipv6_handle_homeaddr, 0)(skb, optoff); -+ break; -+ default: -+ /* Should never happen */ -+ printk(KERN_ERR __FILE__ ": Invalid destination option code (%d)\n", -+ skb->nh.raw[optoff]); -+ ret = 1; -+ break; -+ } -+ -+ /* If mipv6 handlers are not valid, pass the packet to -+ * ip6_tlvopt_unknown() for correct handling. */ -+ if (!ret) -+ return ip6_tlvopt_unknown(skb, optoff); -+ -+ return ret; -+} -+ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/Config.in -@@ -0,0 +1,12 @@ -+# -+# Mobile IPv6 Configuration -+# -+dep_tristate ' IPv6: Mobility Support (Correspondent Node)' CONFIG_IPV6_MOBILITY $CONFIG_IPV6 -+if [ "$CONFIG_IPV6_IPV6_TUNNEL" != "n" ]; then -+ dep_tristate ' MIPv6: Mobile Node Support' CONFIG_IPV6_MOBILITY_MN $CONFIG_IPV6_MOBILITY -+ -+ dep_tristate ' MIPv6: Home Agent Support' CONFIG_IPV6_MOBILITY_HA $CONFIG_IPV6_MOBILITY -+fi -+if [ "$CONFIG_IPV6_MOBILITY" != "n" ]; then -+ bool ' MIPv6: Debug messages' CONFIG_IPV6_MOBILITY_DEBUG -+fi ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/Makefile -@@ -0,0 +1,35 @@ -+# -+# Makefile for the MIPL Mobile IPv6 for Linux. -+# -+# Note! Dependencies are done automagically by 'make dep', which also -+# removes any old dependencies. DON'T put your own dependencies here -+# unless it's something special (ie not a .c file). -+# -+ -+ -+O_TARGET := mip6_base.o -+ -+list-multi := mip6_ha.o mip6_mn.o -+ -+obj-y := hashlist.o bcache.o mobhdr_common.o stats.o exthdrs.o \ -+ rr_crypto.o hmac.o auth_opt.o mipv6_icmp.o module_cn.o -+ -+obj-m := $(O_TARGET) -+ -+mip6_ha-objs := halist.o mipv6_icmp_ha.o tunnel_ha.o \ -+ ndisc_ha.o ha.o module_ha.o -+ -+mip6_mn-objs := mipv6_icmp_mn.o ioctl_mn.o tunnel_mn.o \ -+ mdetect.o bul.o multiaccess_ctl.o mobhdr_mn.o mn.o \ -+ module_mn.o -+ -+obj-$(CONFIG_IPV6_MOBILITY_HA) += mip6_ha.o -+obj-$(CONFIG_IPV6_MOBILITY_MN) += mip6_mn.o -+ -+include $(TOPDIR)/Rules.make -+ -+mip6_ha.o: $(mip6_ha-objs) -+ $(LD) -r -o $@ $(mip6_ha-objs) -+ -+mip6_mn.o: $(mip6_mn-objs) -+ $(LD) -r -o $@ $(mip6_mn-objs) ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/README -@@ -0,0 +1,15 @@ -+MIPL Mobile IPv6 for Linux -+ -+More information at http://www.mipl.mediapoli.com/. -+ -+To join MIPL Mobile IPv6 for Linux mailing lists go to: -+ -+ http://www.mipl.mediapoli.com/cgi-bin/mailman/listinfo -+ -+Or send mail with subject "subscribe" for the general list to: -+ -+ mipl-request@list.mipl.mediapoli.com -+ -+or for the developer list to: -+ -+ mipl-devel-request@list.mail.mediapoli.com ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/auth_opt.c -@@ -0,0 +1,121 @@ -+/* -+ * MIPv6 Binding Authentication Data Option functions -+ * -+ * Authors: -+ * Henrik Petander <lpetande@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/icmpv6.h> -+#include <net/mipv6.h> -+ -+#include "debug.h" -+#include "hmac.h" -+#include "mobhdr.h" -+ -+#define DBG_KEY 5 -+ -+int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *mh, __u8 *aud_data, __u8 *k_bu) -+{ -+ /* First look up the peer from sadb based on his address */ -+ struct ah_processing ahp; -+ -+ /* Don't add any other options or this system is screwed */ -+ -+ __u8 buf[MAX_HASH_LENGTH]; -+ -+ -+ if (!k_bu) { -+ DEBUG(DBG_ERROR, "k_bu missing, aborting"); -+ return -1; -+ } -+ DEBUG(DBG_KEY, "Key for building authenticator:"); -+ debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE); -+ -+ if (ah_hmac_sha1_init(&ahp, k_bu, HMAC_SHA1_KEY_SIZE) < 0) { -+ DEBUG(DBG_ERROR, "Failed to initialize hmac sha1"); -+ return -1; -+ } -+ -+ DEBUG(DBG_KEY, "coa: "); -+ debug_print_buffer(DBG_KEY, coa, 16); -+ DEBUG(DBG_KEY, "cn_addr: "); -+ debug_print_buffer(DBG_KEY, cn_addr, 16); -+ DEBUG(DBG_KEY, "MH contents: "); -+ debug_print_buffer(DBG_KEY, mh, aud_data - mh); -+ -+ /* First the common part */ -+ ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr)); -+ ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr)); -+ ah_hmac_sha1_loop(&ahp, mh, aud_data - mh); -+ ah_hmac_sha1_result(&ahp, buf); -+ -+ memcpy(aud_data, buf, MIPV6_RR_MAC_LENGTH); -+ -+ return 0; -+} -+ -+int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *opt, __u8 optlen, -+ struct mipv6_mo_bauth_data *aud, __u8 *k_bu) -+{ -+ int ret = -1; -+ struct ah_processing ahp; -+ __u8 htarget[MAX_HASH_LENGTH]; -+ -+ /* Look up peer by home address */ -+ if (!k_bu) { -+ DEBUG(DBG_ERROR, "k_bu missing, aborting"); -+ return -1; -+ } -+ -+ DEBUG(DBG_KEY, "Key for checking authenticator:"); -+ debug_print_buffer(DBG_KEY, k_bu, HMAC_SHA1_KEY_SIZE); -+ -+ if (!aud || !coa) { -+ DEBUG(DBG_INFO, "%s is NULL", aud ? "coa" : "aud"); -+ goto out; -+ } -+ -+ if (aud->length != MIPV6_RR_MAC_LENGTH) { -+ DEBUG(DBG_ERROR, -+ ": Incorrect authentication option length %d", aud->length); -+ goto out; -+ } -+ -+ if (ah_hmac_sha1_init(&ahp, k_bu, HMAC_SHA1_KEY_SIZE) < 0) { -+ DEBUG(DBG_ERROR, -+ "internal error in initialization of authentication algorithm"); -+ goto out; -+ } -+ DEBUG(DBG_KEY, "coa: "); -+ debug_print_buffer(DBG_KEY, coa, 16); -+ DEBUG(DBG_KEY, "cn_addr: "); -+ debug_print_buffer(DBG_KEY, cn_addr, 16); -+ DEBUG(DBG_KEY, "MH contents: "); -+ debug_print_buffer(DBG_KEY, opt, (u8*) aud->data - opt); -+ -+ ah_hmac_sha1_loop(&ahp, coa, sizeof(struct in6_addr)); -+ ah_hmac_sha1_loop(&ahp, cn_addr, sizeof(struct in6_addr)); -+ -+ /* -+ * Process MH + options till the start of the authenticator in -+ * Auth. data option -+ */ -+ ah_hmac_sha1_loop(&ahp, opt, (u8 *)aud->data - opt); -+ ah_hmac_sha1_result(&ahp, htarget); -+ if (memcmp(htarget, aud->data, MIPV6_RR_MAC_LENGTH) == 0) -+ ret = 0; -+ -+ DEBUG(DBG_ERROR, "returning %d", ret); -+out: -+ return ret; -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/bcache.c -@@ -0,0 +1,746 @@ -+/* -+ * Binding Cache -+ * -+ * Authors: -+ * Juha Mynttinen <jmynttin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * Changes: -+ * -+ * Nanno Langstraat : Timer code cleaned up, active socket -+ * test rewritten -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/spinlock.h> -+#include <linux/proc_fs.h> -+#include <linux/ipv6_route.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/tcp.h> -+#include <net/udp.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "bcache.h" -+#include "hashlist.h" -+#include "debug.h" -+#include "mobhdr.h" -+#include "tunnel.h" -+#include "config.h" -+ -+#define TIMERDELAY HZ/10 -+ -+struct mipv6_bcache { -+ struct hashlist *entries; -+ __u32 size; -+ struct timer_list callback_timer; -+}; -+ -+struct in6_addr_pair { -+ struct in6_addr *a1; -+ struct in6_addr *a2; -+}; -+ -+static rwlock_t bcache_lock = RW_LOCK_UNLOCKED; -+ -+static struct mipv6_bcache bcache; -+ -+static int bcache_proc_info(char *buffer, char **start, off_t offset, -+ int length); -+ -+#define MIPV6_BCACHE_HASHSIZE 32 -+ -+/* Moment of transmission of a BR, in seconds before bcache entry expiry */ -+#define BCACHE_BR_SEND_LEAD 3 -+ -+#define MIPV6_MAX_BRR 3 /* Send 3 BRRs before deleting BC entry */ -+#define MIPV6_BRR_RATE HZ /* Send BRRs once per second */ -+ -+/* -+ * Internal functions. -+ */ -+ -+struct cache_entry_iterator_args { -+ struct mipv6_bce **entry; -+}; -+ -+static int find_first_cache_entry_iterator(void *data, void *args, -+ unsigned long *lifetime) -+{ -+ struct mipv6_bce *entry = -+ (struct mipv6_bce *) data; -+ struct cache_entry_iterator_args *state = -+ (struct cache_entry_iterator_args *) args; -+ -+ ASSERT(entry != NULL); -+ -+ if (entry->type == CACHE_ENTRY) { -+ *(state->entry) = entry; -+ return ITERATOR_STOP; /* stop iteration */ -+ } else { -+ return ITERATOR_CONT; /* continue iteration */ -+ } -+} -+ -+ -+/* -+ * Get memory for a new bcache entry. If bcache is full, a cache -+ * entry may be deleted to get space for a home registration, but not -+ * vice versa. -+ */ -+static struct mipv6_bce *mipv6_bce_alloc(__u8 type) -+{ -+ struct mipv6_bce *entry; -+ struct cache_entry_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ entry = (struct mipv6_bce *) -+ hashlist_alloc(bcache.entries, SLAB_ATOMIC); -+ -+ /* Cache replacement policy: always replace the CACHE_ENTRY -+ closest to expiration. Type HOME_REGISTRATION entry may -+ never be deleted before expiration. */ -+ if (entry == NULL) { -+ /* cache full, try to delete a CACHE_ENTRY */ -+ args.entry = &entry; -+ hashlist_iterate(bcache.entries, &args, -+ find_first_cache_entry_iterator); -+ if (entry == NULL) -+ return NULL; -+ hashlist_delete(bcache.entries, -+ (struct hashlist_entry *)entry); -+ entry = (struct mipv6_bce *) -+ hashlist_alloc(bcache.entries, SLAB_ATOMIC); -+ } -+ return entry; -+} -+ -+/* -+ * Frees entry's memory allocated with mipv6_bce_alloc -+ */ -+static void mipv6_bce_free(struct mipv6_bce *entry) -+{ -+ hashlist_free(bcache.entries, (void *) entry); -+} -+ -+/* -+ * Removes all expired entries -+ */ -+static void expire(void) -+{ -+ struct mipv6_bce *entry; -+ struct br_addrs { -+ struct in6_addr daddr; -+ struct in6_addr saddr; -+ struct br_addrs *next; -+ }; -+ struct br_addrs *br_info = NULL; -+ -+ DEBUG_FUNC(); -+ -+ write_lock(&bcache_lock); -+ -+ while ((entry = (struct mipv6_bce *) -+ hashlist_get_first(bcache.entries)) != NULL) { -+ struct rt6_info *rt; -+ if (time_after_eq(jiffies, entry->callback_time)) { -+ -+ DEBUG(DBG_INFO, "an entry expired"); -+ -+ if (entry->type & HOME_REGISTRATION) { -+ mip6_fn.proxy_del(&entry->home_addr, entry); -+ } -+ hashlist_delete(bcache.entries, (void *)entry); -+ mipv6_bce_free(entry); -+ entry = NULL; -+ } else if (entry->br_callback_time != 0 && -+ time_after_eq(jiffies, entry->br_callback_time) && -+ entry->br_count < MIPV6_MAX_BRR && -+ (rt = rt6_lookup(&entry->home_addr, &entry->our_addr, 0, 0)) != NULL){ -+ /* Do we have a destination cache entry for the home address */ -+ if (rt->rt6i_flags & RTF_CACHE) { -+ struct br_addrs *tmp; -+ tmp = br_info; -+ DEBUG(DBG_INFO, -+ "bcache entry recently used. Sending BR."); -+ /* queue for sending */ -+ br_info = kmalloc(sizeof(struct br_addrs), -+ GFP_ATOMIC); -+ if (br_info) { -+ ipv6_addr_copy(&br_info->saddr, -+ &entry->our_addr); -+ ipv6_addr_copy(&br_info->daddr, -+ &entry->home_addr); -+ br_info->next = tmp; -+ entry->last_br = jiffies; -+ entry->br_callback_time = jiffies + MIPV6_BRR_RATE; -+ entry->br_count++; -+ } else { -+ br_info = tmp; -+ DEBUG(DBG_ERROR, "Out of memory"); -+ } -+ -+ } else -+ entry->br_callback_time = 0; -+ dst_release(&rt->u.dst); -+ } else { -+ entry->br_callback_time = 0; -+ break; -+ } -+ } -+ write_unlock(&bcache_lock); -+ -+ while (br_info) { -+ struct br_addrs *tmp = br_info->next; -+ if (mipv6_send_brr(&br_info->saddr, &br_info->daddr, NULL) < 0) -+ DEBUG(DBG_WARNING, -+ "BR send for %x:%x:%x:%x:%x:%x:%x:%x failed", -+ NIPV6ADDR(&br_info->daddr)); -+ kfree(br_info); -+ br_info = tmp; -+ } -+} -+ -+static void set_timer(void) -+{ -+ struct mipv6_bce *entry; -+ unsigned long callback_time; -+ -+ DEBUG_FUNC(); -+ -+ entry = (struct mipv6_bce *) -+ hashlist_get_first(bcache.entries); -+ if (entry != NULL) { -+ if (entry->br_callback_time > 0 && -+ time_after(entry->br_callback_time, jiffies)) -+ callback_time = entry->br_callback_time; -+ else if (time_after(entry->callback_time, jiffies)) -+ callback_time = entry->callback_time; -+ else { -+ DEBUG(DBG_WARNING, -+ "bcache timer attempted to schedule" -+ " for a historical jiffies count!"); -+ callback_time = jiffies + TIMERDELAY; -+ } -+ -+ DEBUG(DBG_INFO, "setting timer to now"); -+ mod_timer(&bcache.callback_timer, callback_time); -+ } else { -+ del_timer(&bcache.callback_timer); -+ DEBUG(DBG_INFO, "BC empty, not setting a new timer"); -+ } -+} -+ -+/* -+ * The function that is scheduled to do the callback functions. May be -+ * modified e.g to allow Binding Requests, now only calls expire() and -+ * schedules a new timer. -+ */ -+static void timer_handler(unsigned long dummy) -+{ -+ expire(); -+ write_lock(&bcache_lock); -+ set_timer(); -+ write_unlock(&bcache_lock); -+} -+ -+/* -+ * Interface functions visible to other modules -+ */ -+ -+/** -+ * mipv6_bcache_add - add Binding Cache entry -+ * @ifindex: interface index -+ * @our_addr: own address -+ * @home_addr_org: MN's home address -+ * @coa: MN's care-of address -+ * @lifetime: lifetime for this binding -+ * @prefix: prefix length -+ * @seq: sequence number -+ * @flags: flags received in BU -+ * @type: type of entry -+ * -+ * Adds an entry for this @home_addr_org in the Binding Cache. If entry -+ * already exists, old entry is updated. @type may be %CACHE_ENTRY or -+ * %HOME_REGISTRATION. -+ **/ -+int mipv6_bcache_add(int ifindex, -+ struct in6_addr *our_addr, -+ struct in6_addr *home_addr, -+ struct in6_addr *coa, -+ __u32 lifetime, __u16 seq, __u8 flags, __u8 type) -+{ -+ struct mipv6_bce *entry; -+ int update = 0; -+ int create_tunnel = 0; -+ unsigned long now = jiffies; -+ struct in6_addr_pair hashkey; -+ int ret = -1; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ write_lock(&bcache_lock); -+ -+ if (type == HOME_REGISTRATION && !(mip6node_cnf.capabilities&CAP_HA)) -+ return 0; -+ -+ if (unlikely(bcache.entries == NULL)) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ if ((entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) != NULL) { -+ /* if an entry for this home_addr exists (with smaller -+ * seq than the new seq), update it by removing it -+ * first -+ */ -+ if (!MIPV6_SEQ_GT(seq, entry->seq)) { -+ DEBUG(DBG_INFO, "smaller seq than existing, not updating"); -+ goto out; -+ } -+ DEBUG(DBG_INFO, "updating an existing entry"); -+ update = 1; -+ -+ /* H-flag is already checked in BU handler. */ -+ /* XXX: Should we care about the other flags?*/ -+ if (flags != entry->flags) { -+ DEBUG(DBG_INFO, "entry/BU flag mismatch"); -+ } -+ -+ if (type == HOME_REGISTRATION) { -+ create_tunnel = (ipv6_addr_cmp(&entry->coa, coa) || -+ entry->ifindex != ifindex); -+ } -+ } else { -+ /* no entry for this home_addr, try to create a new entry */ -+ DEBUG(DBG_INFO, "creating a new entry"); -+ update = 0; -+ -+ entry = mipv6_bce_alloc(type); -+ if (entry == NULL) { -+ DEBUG(DBG_INFO, "cache full, entry not added"); -+ goto err; -+ } -+ -+ create_tunnel = (type == HOME_REGISTRATION); -+ } -+ -+ if (create_tunnel) { -+ if (update) -+ mip6_fn.proxy_del(&entry->home_addr, entry); -+ if (mip6_fn.proxy_create(flags, ifindex, coa, our_addr, home_addr) < 0) { -+ goto err_proxy; -+ } -+ } -+ -+ ipv6_addr_copy(&(entry->our_addr), our_addr); -+ ipv6_addr_copy(&(entry->home_addr), home_addr); -+ ipv6_addr_copy(&(entry->coa), coa); -+ entry->ifindex = ifindex; -+ entry->seq = seq; -+ entry->type = type; -+ entry->flags = flags; -+ -+ entry->last_br = 0; -+ entry->destunr_count = 0; -+ entry->callback_time = now + lifetime * HZ; -+ if (entry->type & HOME_REGISTRATION) -+ entry->br_callback_time = 0; -+ else -+ entry->br_callback_time = now + -+ (lifetime - BCACHE_BR_SEND_LEAD) * HZ; -+ -+ if (update) { -+ DEBUG(DBG_INFO, "updating entry : %x", entry); -+ hashlist_reposition(bcache.entries, (void *)entry, -+ entry->callback_time); -+ } else { -+ DEBUG(DBG_INFO, "adding entry: %x", entry); -+ if ((hashlist_add(bcache.entries, -+ &hashkey, -+ entry->callback_time, entry)) < 0) { -+ -+ DEBUG(DBG_ERROR, "Hash add failed"); -+ goto err_hashlist; -+ } -+ } -+ -+ set_timer(); -+ -+out: -+ write_unlock(&bcache_lock); -+ return 0; -+ -+err_hashlist: -+ if (create_tunnel) { -+ mip6_fn.proxy_del(home_addr, entry); -+ } -+err_proxy: -+ if (update) { -+ hashlist_delete(bcache.entries, (void *)entry); -+ } -+ mipv6_bce_free(entry); -+err: -+ write_unlock(&bcache_lock); -+ return ret; -+} -+ -+int mipv6_bcache_icmp_err(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ int destunr_count) -+{ -+ struct mipv6_bce *entry; -+ struct in6_addr_pair hashkey; -+ -+ int ret = -ENOENT; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ write_lock(&bcache_lock); -+ if (unlikely(bcache.entries == NULL)) { -+ ret = -ENOMEM; -+ goto err; -+ } -+ -+ if ((entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) != NULL) { -+ entry->last_destunr = jiffies; -+ entry->destunr_count = destunr_count; -+ ret = 0; -+ } -+err: -+ write_unlock(&bcache_lock); -+ return ret; -+} -+ -+ -+/** -+ * mipv6_bcache_delete - delete Binding Cache entry -+ * @home_addr: MN's home address -+ * @our_addr: our address -+ * @type: type of entry -+ * -+ * Deletes an entry associated with @home_addr from Binding Cache. -+ * Valid values for @type are %CACHE_ENTRY, %HOME_REGISTRATION and -+ * %ANY_ENTRY. %ANY_ENTRY deletes any type of entry. -+ **/ -+int mipv6_bcache_delete(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, __u8 type) -+{ -+ struct mipv6_bce *entry; -+ struct in6_addr_pair hashkey; -+ int err = 0; -+ -+ DEBUG_FUNC(); -+ -+ if (home_addr == NULL || our_addr == NULL) { -+ DEBUG(DBG_INFO, "error in arguments"); -+ return -EINVAL; -+ } -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ write_lock(&bcache_lock); -+ -+ if (unlikely(bcache.entries == NULL) || -+ (entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) == NULL || -+ !(entry->type & type)) { -+ DEBUG(DBG_INFO, "No matching entry found"); -+ err = -ENOENT; -+ goto out; -+ } -+ -+ hashlist_delete(bcache.entries, (void *) entry); -+ mipv6_bce_free(entry); -+ -+ set_timer(); -+out: -+ write_unlock(&bcache_lock); -+ return err; -+} -+ -+/** -+ * mipv6_bcache_exists - check if entry exists -+ * @home_addr: home address to check -+ * @our_addr: our address -+ * -+ * Determines if a binding exists for @home_addr. Returns type of the -+ * entry or negative if entry does not exist. -+ **/ -+int mipv6_bcache_exists(struct in6_addr *home_addr, -+ struct in6_addr *our_addr) -+{ -+ struct mipv6_bce *entry; -+ struct in6_addr_pair hashkey; -+ int type = -ENOENT; -+ -+ DEBUG_FUNC(); -+ -+ if (home_addr == NULL || our_addr == NULL) -+ return -EINVAL; -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ read_lock(&bcache_lock); -+ if (likely(bcache.entries != NULL) && -+ (entry = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey)) != NULL) { -+ type = entry->type; -+ } -+ read_unlock(&bcache_lock); -+ -+ return type; -+} -+ -+/** -+ * mipv6_bcache_get - get entry from Binding Cache -+ * @home_addr: home address to search -+ * @our_addr: our address -+ * @entry: pointer to buffer -+ * -+ * Gets a copy of Binding Cache entry for @home_addr. If entry -+ * exists entry is copied to @entry and zero is returned. -+ * Otherwise returns negative. -+ **/ -+int mipv6_bcache_get(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ struct mipv6_bce *entry) -+{ -+ struct mipv6_bce *entry2; -+ struct in6_addr_pair hashkey; -+ int ret = -ENOENT; -+ -+ DEBUG_FUNC(); -+ -+ if (home_addr == NULL || our_addr == NULL || entry == NULL) -+ return -EINVAL; -+ -+ hashkey.a1 = home_addr; -+ hashkey.a2 = our_addr; -+ -+ read_lock_bh(&bcache_lock); -+ -+ entry2 = (struct mipv6_bce *) -+ hashlist_get(bcache.entries, &hashkey); -+ if (entry2 != NULL) { -+ memcpy(entry, entry2, sizeof(struct mipv6_bce)); -+ ret = 0; -+ } -+ read_unlock_bh(&bcache_lock); -+ return ret; -+} -+ -+int mipv6_bcache_iterate(hashlist_iterator_t func, void *args) -+{ -+ int ret; -+ -+ read_lock_bh(&bcache_lock); -+ ret = hashlist_iterate(bcache.entries, args, func); -+ read_unlock_bh(&bcache_lock); -+ -+ return ret; -+} -+ -+/* -+ * Proc-filesystem functions -+ */ -+ -+#define BC_INFO_LEN 80 -+ -+struct procinfo_iterator_args { -+ char *buffer; -+ int offset; -+ int length; -+ int skip; -+ int len; -+}; -+ -+static int procinfo_iterator(void *data, void *args, unsigned long *pref) -+{ -+ struct procinfo_iterator_args *arg = -+ (struct procinfo_iterator_args *) args; -+ struct mipv6_bce *entry = -+ (struct mipv6_bce *) data; -+ -+ ASSERT(entry != NULL); -+ -+ if (arg->skip < arg->offset / BC_INFO_LEN) { -+ arg->skip++; -+ return ITERATOR_CONT; -+ } -+ -+ if (arg->len >= arg->length) -+ return ITERATOR_CONT; -+ -+ /* HoA CoA CallbackInSecs Type */ -+ arg->len += sprintf(arg->buffer + arg->len, -+ "%08x%08x%08x%08x %08x%08x%08x%08x %010lu %02d\n", -+ ntohl(entry->home_addr.s6_addr32[0]), -+ ntohl(entry->home_addr.s6_addr32[1]), -+ ntohl(entry->home_addr.s6_addr32[2]), -+ ntohl(entry->home_addr.s6_addr32[3]), -+ ntohl(entry->coa.s6_addr32[0]), -+ ntohl(entry->coa.s6_addr32[1]), -+ ntohl(entry->coa.s6_addr32[2]), -+ ntohl(entry->coa.s6_addr32[3]), -+ ((entry->callback_time) - jiffies) / HZ, -+ (int) entry->type); -+ -+ return ITERATOR_CONT; -+} -+ -+ /* -+ * Callback function for proc filesystem. -+ */ -+static int bcache_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct procinfo_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.buffer = buffer; -+ args.offset = offset; -+ args.length = length; -+ args.skip = 0; -+ args.len = 0; -+ -+ read_lock_bh(&bcache_lock); -+ hashlist_iterate(bcache.entries, &args, procinfo_iterator); -+ read_unlock_bh(&bcache_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % BC_INFO_LEN; -+ -+ args.len -= offset % BC_INFO_LEN; -+ -+ if (args.len > length) -+ args.len = length; -+ if (args.len < 0) -+ args.len = 0; -+ -+ return args.len; -+} -+ -+static int bcache_compare(void *data, void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey; -+ struct mipv6_bce *e = (struct mipv6_bce *) data; -+ -+ if (ipv6_addr_cmp(&e->home_addr, p->a1) == 0 -+ && ipv6_addr_cmp(&e->our_addr, p->a2) == 0) -+ return 0; -+ else -+ return -1; -+} -+ -+static __u32 bcache_hash(void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *) hashkey; -+ -+ return p->a1->s6_addr32[0] ^ p->a1->s6_addr32[1] ^ -+ p->a2->s6_addr32[2] ^ p->a2->s6_addr32[3]; -+} -+ -+/* -+ * Initialization and shutdown functions -+ */ -+ -+int __init mipv6_bcache_init(__u32 size) -+{ -+ if (size < 1) { -+ DEBUG(DBG_ERROR, "Binding cache size must be at least 1"); -+ return -EINVAL; -+ } -+ bcache.entries = hashlist_create(MIPV6_BCACHE_HASHSIZE, size, -+ sizeof(struct mipv6_bce), -+ "mip6_bcache", NULL, NULL, -+ bcache_compare, bcache_hash); -+ -+ if (bcache.entries == NULL) { -+ DEBUG(DBG_ERROR, "Failed to initialize hashlist"); -+ return -ENOMEM; -+ } -+ -+ init_timer(&bcache.callback_timer); -+ bcache.callback_timer.data = 0; -+ bcache.callback_timer.function = timer_handler; -+ bcache.size = size; -+ -+ proc_net_create("mip6_bcache", 0, bcache_proc_info); -+ -+ DEBUG(DBG_INFO, "Binding cache initialized"); -+ return 0; -+} -+ -+static int -+bce_cleanup_iterator(void *rawentry, void *args, unsigned long *sortkey) -+{ -+ int type = (int) args; -+ struct mipv6_bce *entry = (struct mipv6_bce *) rawentry; -+ if (entry->type == type) { -+ if (entry->type & HOME_REGISTRATION) { -+ if (unlikely(mip6_fn.proxy_del == NULL)) -+ DEBUG(DBG_ERROR, "proxy_del unitialized"); -+ else -+ mip6_fn.proxy_del(&entry->home_addr, entry); -+ } -+ return ITERATOR_DELETE_ENTRY; -+ } -+ return ITERATOR_CONT; -+ -+} -+ -+void mipv6_bcache_cleanup(int type) -+{ -+ write_lock_bh(&bcache_lock); -+ hashlist_iterate(bcache.entries,(void *) type, bce_cleanup_iterator); -+ write_unlock_bh(&bcache_lock); -+} -+ -+int __exit mipv6_bcache_exit(void) -+{ -+ struct hashlist *entries; -+ -+ DEBUG_FUNC(); -+ -+ proc_net_remove("mip6_bcache"); -+ -+ write_lock_bh(&bcache_lock); -+ DEBUG(DBG_INFO, "Stopping the bcache timer"); -+ del_timer(&bcache.callback_timer); -+ hashlist_iterate(bcache.entries,(void *)CACHE_ENTRY, -+ bce_cleanup_iterator); -+ -+ entries = bcache.entries; -+ bcache.entries = NULL; -+ write_unlock_bh(&bcache_lock); -+ -+ hashlist_destroy(entries); -+ return 0; -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/bcache.h -@@ -0,0 +1,72 @@ -+/* -+ * MIPL Mobile IPv6 Binding Cache header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _BCACHE_H -+#define _BCACHE_H -+ -+#include <linux/in6.h> -+#include <linux/timer.h> -+#include "hashlist.h" -+ -+#define CACHE_ENTRY 1 /* this and HOME_REGISTRATION are the entry types */ -+#define HOME_REGISTRATION 2 -+#define ANY_ENTRY 3 -+ -+#define MIPV6_MAX_DESTUNREACH 5 /* Delete CN BCEs after 5 destination unreachables */ -+#define MIPV6_DEST_UNR_IVAL 10 /* What is the max interval of destination -+ unreacahable error messages for them to be persistent*/ -+ -+struct mipv6_bce { -+ struct hashlist_entry e; -+ int ifindex; /* Interface identifier */ -+ struct in6_addr our_addr; /* our address (as seen by the MN) */ -+ struct in6_addr home_addr; /* MN home address */ -+ struct in6_addr coa; /* MN care-of address */ -+ unsigned long callback_time; /* time of expiration (in jiffies) */ -+ unsigned long br_callback_time; /* time for sending a BR (in jiffies) */ -+ int (*callback_function)(struct mipv6_bce *entry); -+ __u8 type; /* home registration */ -+ __u8 router; /* mn is router */ -+ __u8 flags; /* flags received in BU */ -+ __u16 seq; /* sequence number */ -+ unsigned long last_br; /* time when last BR sent */ -+ unsigned long last_destunr; /* time when last ICMP destination unreachable received */ -+ int br_count; /* How many BRRs have sent */ -+ int destunr_count; /* Number of destination unreachables received */ -+}; -+ -+int mipv6_bcache_add(int ifindex, struct in6_addr *our_addr, -+ struct in6_addr *home_addr, struct in6_addr *coa, -+ __u32 lifetime, __u16 seq, __u8 flags, __u8 type); -+ -+int mipv6_bcache_icmp_err(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ int destunr_count); -+ -+int mipv6_bcache_delete(struct in6_addr *home_addr, struct in6_addr *our_addr, -+ __u8 type); -+ -+int mipv6_bcache_exists(struct in6_addr *home_addr, -+ struct in6_addr *our_addr); -+ -+int mipv6_bcache_get(struct in6_addr *home_addr, -+ struct in6_addr *our_addr, -+ struct mipv6_bce *entry); -+ -+int mipv6_bcache_iterate(int (*func)(void *, void *, unsigned long *), void *args); -+ -+void mipv6_bcache_cleanup(int type); -+ -+int mipv6_bcache_init(__u32 size); -+ -+int mipv6_bcache_exit(void); -+ -+#endif /* _BCACHE_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/bul.c -@@ -0,0 +1,634 @@ -+/* -+ * Binding update list -+ * -+ * Authors: -+ * Juha Mynttinen <jmynttin@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/* -+ * Changes: -+ * -+ * Nanno Langstraat : Timer code cleaned up -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/spinlock.h> -+#include <net/ipv6.h> -+#include <net/mipv6.h> -+#include <linux/proc_fs.h> -+ -+#include "bul.h" -+#include "debug.h" -+#include "hashlist.h" -+#include "tunnel_mn.h" -+#include "mobhdr.h" -+ -+#define MIPV6_BUL_HASHSIZE 32 -+ -+rwlock_t bul_lock = RW_LOCK_UNLOCKED; -+ -+struct mipv6_bul { -+ struct hashlist *entries; -+ struct timer_list callback_timer; -+}; -+ -+static struct mipv6_bul bul; -+ -+struct in6_addr_pair { -+ struct in6_addr *a1; -+ struct in6_addr *a2; -+}; -+ -+/********************************************************************** -+ * -+ * Private functions -+ * -+ **********************************************************************/ -+ -+static int bul_compare(void *data, void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey; -+ struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data; -+ -+ if (ipv6_addr_cmp(&e->cn_addr, p->a1) == 0 -+ && ipv6_addr_cmp(&e->home_addr, p->a2) == 0) -+ return 0; -+ else -+ return -1; -+} -+ -+struct test_keys { -+ struct in6_addr *addr; -+ u8 *cookie; -+}; -+ -+static int bul_compare_cookie(void *data, void *keys) -+{ -+ struct test_keys *p = (struct test_keys *)keys; -+ struct mipv6_bul_entry *e = (struct mipv6_bul_entry *)data; -+ -+ if (ipv6_addr_cmp(&e->cn_addr, p->addr) == 0 && e->rr -+ && memcmp(&e->rr->cot_cookie, p->cookie, 8) == 0) -+ return 0; -+ else -+ return -1; -+} -+ -+static u32 bul_hash(void *hashkey) -+{ -+ struct in6_addr_pair *p = (struct in6_addr_pair *)hashkey; -+ -+ return p->a1->s6_addr32[0] ^ -+ p->a1->s6_addr32[1] ^ -+ p->a1->s6_addr32[2] ^ -+ p->a1->s6_addr32[3]; -+} -+ -+static int bul_proc_info(char *buffer, char **start, off_t offset, -+ int length); -+ -+static struct mipv6_bul_entry *mipv6_bul_get_entry(void) -+{ -+ DEBUG_FUNC(); -+ return ((struct mipv6_bul_entry *) -+ hashlist_alloc(bul.entries, SLAB_ATOMIC)); -+} -+ -+static void mipv6_bul_entry_free(struct mipv6_bul_entry *entry) -+{ -+ DEBUG_FUNC(); -+ -+ if (entry->rr) { -+ if (entry->rr->kbu) -+ kfree(entry->rr->kbu); -+ kfree(entry->rr); -+ } -+ if (entry->ops) -+ kfree(entry->ops); -+ hashlist_free(bul.entries, (void *)entry); -+} -+ -+static __inline__ int del_bul_entry_tnl(struct mipv6_bul_entry *entry) -+{ -+ if (entry->flags & MIPV6_BU_F_HOME) { -+ return mipv6_mv_tnl_to_ha(&entry->cn_addr, -+ &entry->coa, -+ &entry->home_addr); -+ } -+ return 0; -+} -+ -+static void timer_update(void) -+{ -+ struct mipv6_bul_entry *entry; -+ -+ DEBUG_FUNC(); -+ -+ entry = hashlist_get_first(bul.entries); -+ -+ while (entry && time_after_eq(jiffies, entry->callback_time)) { -+ if (time_after_eq(jiffies, entry->expire) || -+ entry->callback(entry) != 0) { -+ /* -+ * Either the entry has expired, or the callback -+ * indicated that it should be deleted. -+ */ -+ hashlist_delete(bul.entries, (void *)entry); -+ -+ del_bul_entry_tnl(entry); -+ mipv6_bul_entry_free(entry); -+ DEBUG(DBG_INFO, "Entry deleted (was expired) from " -+ "binding update list"); -+ } else { -+ /* move entry to its right place in the hashlist */ -+ DEBUG(DBG_INFO, "Rescheduling"); -+ hashlist_reposition(bul.entries, (void *)entry, -+ entry->callback_time); -+ } -+ entry = (struct mipv6_bul_entry *) -+ hashlist_get_first(bul.entries); -+ } -+ -+ if (entry == NULL) { -+ DEBUG(DBG_INFO, "bul empty, not setting a new timer"); -+ del_timer(&bul.callback_timer); -+ } else { -+ mod_timer(&bul.callback_timer, entry->callback_time); -+ } -+} -+ -+static void timer_handler(unsigned long dummy) -+{ -+ DEBUG_FUNC(); -+ -+ write_lock(&bul_lock); -+ timer_update(); -+ write_unlock(&bul_lock); -+} -+ -+/********************************************************************** -+ * -+ * Public interface functions -+ * -+ **********************************************************************/ -+ -+/** -+ * mipv6_bul_iterate - apply interator function to all entries -+ * @func: function to apply -+ * @args: extra arguments for iterator -+ * -+ * Applies @func for each entry in Binding Update List. Extra -+ * arguments given in @args are also passed to the iterator function. -+ * Caller must hold @bul_lock. -+ **/ -+int mipv6_bul_iterate(hashlist_iterator_t func, void *args) -+{ -+ DEBUG_FUNC(); -+ -+ return hashlist_iterate(bul.entries, args, func); -+} -+ -+/** -+ * mipv6_bul_exists - check if Binding Update List entry exists -+ * @cn: address to check -+ * -+ * Checks if Binding Update List has an entry for @cn. Returns true -+ * if entry exists, false otherwise. Caller may not hold @bul_lock. -+ **/ -+int mipv6_bul_exists(struct in6_addr *cn, struct in6_addr *haddr) -+{ -+ int exists; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = cn; -+ hashkey.a2 = haddr; -+ -+ read_lock_bh(&bul_lock); -+ -+ if (unlikely(bul.entries == NULL)) -+ exists = 0; -+ else -+ exists = (hashlist_get(bul.entries, &hashkey) != NULL); -+ -+ read_unlock_bh(&bul_lock); -+ return exists; -+} -+ -+/** -+ * mipv6_bul_get - get Binding Update List entry -+ * @cn_addr: CN address to search -+ * @home_addr: home address to search -+ * -+ * Returns Binding Update List entry for @cn_addr if it exists. -+ * Otherwise returns %NULL. Caller must hold @bul_lock. -+ **/ -+struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cn_addr, -+ struct in6_addr *home_addr) -+{ -+ struct mipv6_bul_entry *entry; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ if (unlikely(bul.entries == NULL)) { -+ return NULL; -+ } -+ hashkey.a1 = cn_addr; -+ hashkey.a2 = home_addr; -+ -+ entry = (struct mipv6_bul_entry *) -+ hashlist_get(bul.entries, &hashkey); -+ -+ return entry; -+} -+ -+struct mipv6_bul_entry *mipv6_bul_get_by_ccookie( -+ struct in6_addr *cn_addr, u8 *cookie) -+{ -+ struct test_keys key; -+ -+ DEBUG_FUNC(); -+ -+ if (unlikely(bul.entries == NULL)) -+ return NULL; -+ key.addr = cn_addr; -+ key.cookie = cookie; -+ -+ return (struct mipv6_bul_entry *) -+ hashlist_get_ex(bul.entries, &key, -+ bul_compare_cookie); -+} -+ -+/** -+ * mipv6_bul_reschedule - reschedule Binding Update List entry -+ * @entry: entry to reschedule -+ * -+ * Reschedules a Binding Update List entry. Must be called after -+ * modifying entry lifetime. Caller must hold @bul_lock (write). -+ **/ -+void mipv6_bul_reschedule(struct mipv6_bul_entry *entry) -+{ -+ DEBUG_FUNC(); -+ -+ hashlist_reposition(bul.entries, -+ (void *)entry, -+ entry->callback_time); -+ timer_update(); -+} -+ -+/** -+ * mipv6_bul_add - add binding update to Binding Update List -+ * @cn_addr: IPv6 address where BU was sent -+ * @home_addr: Home address for this binding -+ * @coa: Care-of address for this binding -+ * @lifetime: expiration time of the binding in seconds -+ * @seq: sequence number of the BU -+ * @flags: %MIPV6_BU_F_* flags -+ * @callback: callback function called on expiration -+ * @callback_time: expiration time for callback -+ * @state: binding send state -+ * @delay: retransmission delay -+ * @maxdelay: retransmission maximum delay -+ * @ops: Mobility header options for BU -+ * @rr: Return routability information -+ * -+ * Adds a binding update sent to @cn_addr for @home_addr to the -+ * Binding Update List. If entry already exists, it is updated. -+ * Entry is set to expire in @lifetime seconds. Entry has a callback -+ * function @callback that is called at @callback_time. Entry @state -+ * controls resending of this binding update and it can be set to -+ * %ACK_OK, %RESEND_EXP or %ACK_ERROR. Returns a pointer to the newly -+ * created or updated entry. Caller must hold @bul_lock (write). -+ **/ -+struct mipv6_bul_entry *mipv6_bul_add( -+ struct in6_addr *cn_addr, struct in6_addr *home_addr, -+ struct in6_addr *coa, -+ __u32 lifetime, __u16 seq, __u8 flags, -+ int (*callback)(struct mipv6_bul_entry *entry), -+ __u32 callback_time, -+ __u8 state, __u32 delay, __u32 maxdelay, -+ struct mipv6_mh_opt *ops, -+ struct mipv6_rr_info *rr) -+{ -+ struct mipv6_bul_entry *entry; -+ int update = 0; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ if (unlikely(bul.entries == NULL)) -+ return NULL; -+ -+ if (cn_addr == NULL || home_addr == NULL || coa == NULL || -+ lifetime < 0 || callback == NULL || callback_time < 0 || -+ (state != ACK_OK && state != RESEND_EXP && state != ACK_ERROR) || -+ delay < 0 || maxdelay < 0) { -+ DEBUG(DBG_ERROR, "invalid arguments"); -+ return NULL; -+ } -+ DEBUG(DBG_INFO, "cn_addr: %x:%x:%x:%x:%x:%x:%x:%x, " -+ "home_addr: %x:%x:%x:%x:%x:%x:%x:%x" -+ "coaddr: %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(cn_addr), -+ NIPV6ADDR(home_addr), NIPV6ADDR(coa)); -+ hashkey.a1 = cn_addr; -+ hashkey.a2 = home_addr; -+ -+ /* -+ * decide whether to add a new entry or update existing, also -+ * check if there's room for a new entry when adding a new -+ * entry (latter is handled by mipv6_bul_get_entry() -+ */ -+ if ((entry = (struct mipv6_bul_entry *) -+ hashlist_get(bul.entries, &hashkey)) != NULL) { -+ /* if an entry for this cn_addr exists (with smaller -+ * seq than the new entry's seq), update it */ -+ -+ if (MIPV6_SEQ_GT(seq, entry->seq)) { -+ DEBUG(DBG_INFO, "updating an existing entry"); -+ update = 1; -+ } else { -+ DEBUG(DBG_INFO, "smaller seq than existing, not updating"); -+ return NULL; -+ } -+ } else { -+ entry = mipv6_bul_get_entry(); -+ if (entry == NULL) { -+ DEBUG(DBG_WARNING, "binding update list full, can't add!!!"); -+ return NULL; -+ } -+ memset(entry, 0, sizeof(*entry)); -+ /* First BU send happens here, save count in the entry */ -+ entry->consecutive_sends = 1; -+ } -+ -+ if (!update) { -+ ipv6_addr_copy(&(entry->cn_addr), cn_addr); -+ ipv6_addr_copy(&(entry->home_addr), home_addr); -+ entry->ops = ops; -+ } -+ /* Add Return Routability info to bul entry */ -+ if (rr) { -+ if(entry->rr) -+ kfree(entry->rr); -+ entry->rr = rr; -+ } -+ -+ ipv6_addr_copy(&(entry->coa), coa); -+ entry->lifetime = lifetime; -+ if (lifetime) -+ entry->expire = jiffies + lifetime * HZ; -+ else if (flags & MIPV6_BU_F_ACK) -+ entry->expire = jiffies + HOME_RESEND_EXPIRE * HZ; -+ entry->seq = seq; -+ entry->flags = flags; -+ entry->lastsend = jiffies; /* current time = last use of the entry */ -+ entry->state = state; -+ entry->delay = delay; -+ entry->maxdelay = maxdelay; -+ entry->callback_time = jiffies + callback_time * HZ; -+ entry->callback = callback; -+ -+ if (flags & MIPV6_BU_F_HOME && -+ mipv6_mv_tnl_to_ha(cn_addr, coa, home_addr)) { -+ DEBUG(DBG_ERROR, "reconfiguration of the tunnel failed"); -+ } -+ if (update) { -+ DEBUG(DBG_INFO, "updating entry: %x", entry); -+ hashlist_reposition(bul.entries, (void *)entry, -+ entry->callback_time); -+ } else { -+ DEBUG(DBG_INFO, "adding entry: %x", entry); -+ -+ hashkey.a1 = &entry->cn_addr; -+ hashkey.a2 = &entry->home_addr; -+ -+ if ((hashlist_add(bul.entries, &hashkey, -+ entry->callback_time, -+ entry)) < 0) { -+ DEBUG(DBG_ERROR, "Hash add failed"); -+ mipv6_bul_entry_free(entry); -+ return NULL; -+ } -+ } -+ timer_update(); -+ -+ return entry; -+} -+ -+/** -+ * mipv6_bul_delete - delete Binding Update List entry -+ * @cn_addr: address for entry to delete -+ * -+ * Deletes the entry for @cn_addr from the Binding Update List. -+ * Returns zero if entry was deleted succesfully, otherwise returns -+ * negative. Caller may not hold @bul_lock. -+ **/ -+int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr) -+{ -+ struct mipv6_bul_entry *entry; -+ struct in6_addr_pair hashkey; -+ -+ DEBUG_FUNC(); -+ -+ hashkey.a1 = cn_addr; -+ hashkey.a2 = home_addr; -+ -+ write_lock(&bul_lock); -+ -+ if (unlikely(bul.entries == NULL) || -+ (entry = (struct mipv6_bul_entry *) -+ hashlist_get(bul.entries, &hashkey)) == NULL) { -+ write_unlock(&bul_lock); -+ DEBUG(DBG_INFO, "No such entry"); -+ return -ENOENT; -+ } -+ -+ hashlist_delete(bul.entries, (void *)entry); -+ -+ del_bul_entry_tnl(entry); -+ -+ mipv6_bul_entry_free(entry); -+ timer_update(); -+ write_unlock(&bul_lock); -+ -+ DEBUG(DBG_INFO, "Binding update list entry deleted"); -+ -+ return 0; -+} -+ -+/********************************************************************** -+ * -+ * Proc interface functions -+ * -+ **********************************************************************/ -+ -+#define BUL_INFO_LEN 152 -+ -+struct procinfo_iterator_args { -+ char *buffer; -+ int offset; -+ int length; -+ int skip; -+ int len; -+}; -+ -+static int procinfo_iterator(void *data, void *args, -+ unsigned long *sortkey) -+{ -+ struct procinfo_iterator_args *arg = -+ (struct procinfo_iterator_args *)args; -+ struct mipv6_bul_entry *entry = -+ (struct mipv6_bul_entry *)data; -+ unsigned long callback_seconds; -+ -+ DEBUG_FUNC(); -+ -+ if (entry == NULL) return ITERATOR_ERR; -+ -+ if (time_after(jiffies, entry->callback_time)) -+ callback_seconds = 0; -+ else -+ callback_seconds = (entry->callback_time - jiffies) / HZ; -+ -+ if (arg->skip < arg->offset / BUL_INFO_LEN) { -+ arg->skip++; -+ return ITERATOR_CONT; -+ } -+ -+ if (arg->len >= arg->length) -+ return ITERATOR_CONT; -+ -+ /* CN HoA CoA ExpInSecs SeqNum State Delay MaxDelay CallbackInSecs */ -+ arg->len += sprintf(arg->buffer + arg->len, -+ "%08x%08x%08x%08x %08x%08x%08x%08x %08x%08x%08x%08x\n" -+ "%010lu %05d %02d %010d %010d %010lu\n", -+ ntohl(entry->cn_addr.s6_addr32[0]), -+ ntohl(entry->cn_addr.s6_addr32[1]), -+ ntohl(entry->cn_addr.s6_addr32[2]), -+ ntohl(entry->cn_addr.s6_addr32[3]), -+ ntohl(entry->home_addr.s6_addr32[0]), -+ ntohl(entry->home_addr.s6_addr32[1]), -+ ntohl(entry->home_addr.s6_addr32[2]), -+ ntohl(entry->home_addr.s6_addr32[3]), -+ ntohl(entry->coa.s6_addr32[0]), -+ ntohl(entry->coa.s6_addr32[1]), -+ ntohl(entry->coa.s6_addr32[2]), -+ ntohl(entry->coa.s6_addr32[3]), -+ (entry->expire - jiffies) / HZ, -+ entry->seq, entry->state, entry->delay, -+ entry->maxdelay, callback_seconds); -+ -+ return ITERATOR_CONT; -+} -+ -+ -+/* -+ * Callback function for proc filesystem. -+ */ -+static int bul_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct procinfo_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.buffer = buffer; -+ args.offset = offset; -+ args.length = length; -+ args.skip = 0; -+ args.len = 0; -+ -+ read_lock_bh(&bul_lock); -+ hashlist_iterate(bul.entries, &args, procinfo_iterator); -+ read_unlock_bh(&bul_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % BUL_INFO_LEN; -+ -+ args.len -= offset % BUL_INFO_LEN; -+ -+ if (args.len > length) -+ args.len = length; -+ if (args.len < 0) -+ args.len = 0; -+ -+ return args.len; -+} -+ -+/********************************************************************** -+ * -+ * Code module init/fini functions -+ * -+ **********************************************************************/ -+ -+int __init mipv6_bul_init(__u32 size) -+{ -+ DEBUG_FUNC(); -+ -+ if (size < 1) { -+ DEBUG(DBG_CRITICAL, -+ "Binding update list size must be at least 1"); -+ return -EINVAL; -+ } -+ bul.entries = hashlist_create(MIPV6_BUL_HASHSIZE, size, -+ sizeof(struct mipv6_bul_entry), -+ "mip6_bul", NULL, NULL, -+ bul_compare, bul_hash); -+ -+ if (bul.entries == NULL) { -+ DEBUG(DBG_CRITICAL, "Couldn't allocate memory for " -+ "hashlist when creating a binding update list"); -+ return -ENOMEM; -+ } -+ init_timer(&bul.callback_timer); -+ bul.callback_timer.data = 0; -+ bul.callback_timer.function = timer_handler; -+ proc_net_create("mip6_bul", 0, bul_proc_info); -+ DEBUG(DBG_INFO, "Binding update list initialized"); -+ return 0; -+} -+ -+void __exit mipv6_bul_exit() -+{ -+ struct mipv6_bul_entry *entry; -+ struct hashlist *entries; -+ -+ DEBUG_FUNC(); -+ -+ proc_net_remove("mip6_bul"); -+ -+ write_lock_bh(&bul_lock); -+ -+ DEBUG(DBG_INFO, "Stopping the bul timer"); -+ del_timer(&bul.callback_timer); -+ -+ while ((entry = (struct mipv6_bul_entry *) -+ hashlist_get_first(bul.entries)) != NULL) { -+ hashlist_delete(bul.entries, (void *)entry); -+ -+ del_bul_entry_tnl(entry); -+ -+ mipv6_bul_entry_free(entry); -+ } -+ entries = bul.entries; -+ bul.entries = NULL; -+ write_unlock_bh(&bul_lock); -+ -+ hashlist_destroy(entries); -+ -+ DEBUG(DBG_INFO, "binding update list destroyed"); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/bul.h -@@ -0,0 +1,91 @@ -+/* -+ * MIPL Mobile IPv6 Binding Update List header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _BUL_H -+#define _BUL_H -+ -+#include "hashlist.h" -+ -+#define ACK_OK 0x01 -+#define RESEND_EXP 0x02 -+#define ACK_ERROR 0x04 -+ -+#define HOME_RESEND_EXPIRE 3600 -+#define MIPV6_COOKIE_LEN 8 -+struct mipv6_rr_info { -+ /* RR information */ -+ u16 rr_state; /* State of the RR */ -+ u16 rr_flags; /* Flags for the RR */ -+ u8 hot_cookie[MIPV6_COOKIE_LEN]; /* HoT Cookie */ -+ u8 cot_cookie[MIPV6_COOKIE_LEN]; /* CoT Cookie */ -+ u8 home_cookie[MIPV6_COOKIE_LEN]; /* Home Cookie */ -+ u8 careof_cookie[MIPV6_COOKIE_LEN]; /* Careof Cookie */ -+ u32 lastsend_hoti; /* When HoTI was last sent (jiffies) */ -+ u32 lastsend_coti; /* When CoTI was last sent (jiffies) */ -+ u32 home_time; /* when Care-of cookie was received */ -+ u32 careof_time; /* when Home cookie was received */ -+ int home_nonce_index; /* Home cookie nonce index */ -+ int careof_nonce_index; /* Care-of cookie nonce index */ -+ u8 *kbu; /* Binding authentication key */ -+}; -+struct mipv6_bul_entry { -+ struct hashlist_entry e; -+ struct in6_addr cn_addr; /* CN to which BU was sent */ -+ struct in6_addr home_addr; /* home address of this binding */ -+ struct in6_addr coa; /* care-of address of the sent BU */ -+ -+ unsigned long expire; /* entry's expiration time (jiffies) */ -+ __u32 lifetime; /* lifetime sent in this BU */ -+ __u32 lastsend; /* last time when BU sent (jiffies) */ -+ __u32 consecutive_sends; /* Number of consecutive BU's sent */ -+ __u16 seq; /* sequence number of the latest BU */ -+ __u8 flags; /* BU send flags */ -+ __u8 state; /* resend state */ -+ __u32 initdelay; /* initial ack wait */ -+ __u32 delay; /* current ack wait */ -+ __u32 maxdelay; /* maximum ack wait */ -+ -+ struct mipv6_rr_info *rr; -+ struct mipv6_mh_opt *ops; /* saved option values */ -+ -+ unsigned long callback_time; -+ int (*callback)(struct mipv6_bul_entry *entry); -+}; -+ -+extern rwlock_t bul_lock; -+ -+int mipv6_bul_init(__u32 size); -+ -+void mipv6_bul_exit(void); -+ -+struct mipv6_bul_entry *mipv6_bul_add( -+ struct in6_addr *cn_addr, struct in6_addr *home_addr, -+ struct in6_addr *coa, __u32 lifetime, __u16 seq, __u8 flags, -+ int (*callback)(struct mipv6_bul_entry *entry), __u32 callback_time, -+ __u8 state, __u32 delay, __u32 maxdelay, struct mipv6_mh_opt *ops, -+ struct mipv6_rr_info *rr); -+ -+int mipv6_bul_delete(struct in6_addr *cn_addr, struct in6_addr *home_addr); -+ -+int mipv6_bul_exists(struct in6_addr *cnaddr, struct in6_addr *home_addr); -+ -+struct mipv6_bul_entry *mipv6_bul_get(struct in6_addr *cnaddr, -+ struct in6_addr *home_addr); -+struct mipv6_bul_entry *mipv6_bul_get_by_ccookie(struct in6_addr *cn_addr, -+ u8 *cookie); -+ -+int bul_entry_expired(struct mipv6_bul_entry *bulentry); -+ -+void mipv6_bul_reschedule(struct mipv6_bul_entry *entry); -+ -+int mipv6_bul_iterate(int (*func)(void *, void *, unsigned long *), void *args); -+ -+#endif /* BUL_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/config.h -@@ -0,0 +1,72 @@ -+/* -+ * Configuration parameters -+ * -+ * $Id$ -+ */ -+ -+#define MIPV6VERSION "D24" -+#define MIPLVERSION "v1.0" -+ -+#define CAP_CN 0x01 -+#define CAP_HA 0x02 -+#define CAP_MN 0x04 -+ -+struct mip6_conf { -+ int capabilities; -+ int debug_level; -+ int accept_ret_rout; -+ int max_rtr_reachable_time; -+ int eager_cell_switching; -+ int max_num_tunnels; -+ int min_num_tunnels; -+ int binding_refresh_advice; -+ int bu_lladdr; -+ int bu_keymgm; -+ int bu_cn_ack; -+}; -+ -+extern struct mip6_conf mip6node_cnf; -+ -+struct mipv6_bce; -+ -+struct mip6_func { -+ void (*bce_home_add) (int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ void (*bce_cache_add) (int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ void (*bce_home_del) (struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu); -+ void (*bce_cache_del) (struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu); -+ -+ int (*bce_tnl_rt_add) (struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+ void (*bce_tnl_rt_del) (struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+ void (*proxy_del) (struct in6_addr *home_addr, struct mipv6_bce *entry); -+ int (*proxy_create) (int flags, int ifindex, struct in6_addr *coa, -+ struct in6_addr *our_addr, struct in6_addr *home_addr); -+ -+ int (*icmpv6_dhaad_rep_rcv) (struct sk_buff *skb); -+ int (*icmpv6_dhaad_req_rcv) (struct sk_buff *skb); -+ int (*icmpv6_pfxadv_rcv) (struct sk_buff *skb); -+ int (*icmpv6_pfxsol_rcv) (struct sk_buff *skb); -+ int (*icmpv6_paramprob_rcv) (struct sk_buff *skb); -+ -+ int (*mn_use_hao) (struct in6_addr *daddr, struct in6_addr *saddr); -+ void (*mn_check_tunneled_packet) (struct sk_buff *skb); -+}; -+ -+extern struct mip6_func mip6_fn; ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/debug.h -@@ -0,0 +1,112 @@ -+/* -+ * MIPL Mobile IPv6 Debugging macros and functions -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _DEBUG_H -+#define _DEBUG_H -+ -+#include <linux/autoconf.h> -+ -+/* priorities for different debug conditions */ -+ -+#define DBG_CRITICAL 0 /* unrecoverable error */ -+#define DBG_ERROR 1 /* error (recoverable) */ -+#define DBG_WARNING 2 /* unusual situation but not a real error */ -+#define DBG_INFO 3 /* generally useful information */ -+#define DBG_EXTRA 4 /* extra information */ -+#define DBG_FUNC_ENTRY 6 /* use to indicate function entry and exit */ -+#define DBG_DATADUMP 7 /* packet dumps, etc. lots of flood */ -+ -+/** -+ * NIPV6ADDR - macro for IPv6 addresses -+ * @addr: Network byte order IPv6 address -+ * -+ * Macro for printing IPv6 addresses. Used in conjunction with -+ * printk() or derivatives (such as DEBUG macro). -+ **/ -+#define NIPV6ADDR(addr) \ -+ ntohs(((u16 *)addr)[0]), \ -+ ntohs(((u16 *)addr)[1]), \ -+ ntohs(((u16 *)addr)[2]), \ -+ ntohs(((u16 *)addr)[3]), \ -+ ntohs(((u16 *)addr)[4]), \ -+ ntohs(((u16 *)addr)[5]), \ -+ ntohs(((u16 *)addr)[6]), \ -+ ntohs(((u16 *)addr)[7]) -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+extern int mipv6_debug; -+ -+/** -+ * debug_print - print debug message -+ * @debug_level: message priority -+ * @fname: calling function's name -+ * @fmt: printf-style formatting string -+ * -+ * Prints a debug message to system log if @debug_level is less or -+ * equal to @mipv6_debug. Should always be called using DEBUG() -+ * macro, not directly. -+ **/ -+static void debug_print(int debug_level, const char *fname, const char* fmt, ...) -+{ -+ char s[1024]; -+ va_list args; -+ -+ if (mipv6_debug < debug_level) -+ return; -+ -+ va_start(args, fmt); -+ vsprintf(s, fmt, args); -+ printk("mip6[%s]: %s\n", fname, s); -+ va_end(args); -+} -+ -+/** -+ * debug_print_buffer - print arbitrary buffer to system log -+ * @debug_level: message priority -+ * @data: pointer to buffer -+ * @len: number of bytes to print -+ * -+ * Prints @len bytes from buffer @data to system log. @debug_level -+ * tells on which debug level message gets printed. For -+ * debug_print_buffer() priority %DBG_DATADUMP should be used. -+ **/ -+#define debug_print_buffer(debug_level,data,len) { \ -+ if (mipv6_debug >= debug_level) { \ -+ int i; \ -+ for (i=0; i<len; i++) { \ -+ if (i%16 == 0) printk("\n%04x: ", i); \ -+ printk("%02x ", ((unsigned char *)data)[i]); \ -+ } \ -+ printk("\n\n"); \ -+ } \ -+} -+ -+#define DEBUG(x,y,z...) debug_print(x,__FUNCTION__,y,##z) -+#define DEBUG_FUNC() \ -+DEBUG(DBG_FUNC_ENTRY, "%s(%d)/%s: ", __FILE__,__LINE__,__FUNCTION__) -+ -+#else -+#define DEBUG(x,y,z...) -+#define DEBUG_FUNC() -+#define debug_print_buffer(x,y,z) -+#endif -+ -+#undef ASSERT -+#define ASSERT(expression) { \ -+ if (!(expression)) { \ -+ (void)printk(KERN_ERR \ -+ "Assertion \"%s\" failed: file \"%s\", function \"%s\", line %d\n", \ -+ #expression, __FILE__, __FUNCTION__, __LINE__); \ -+ BUG(); \ -+ } \ -+} -+ -+#endif /* _DEBUG_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/exthdrs.c -@@ -0,0 +1,394 @@ -+/* -+ * Extension Header handling and adding code -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/types.h> -+#include <linux/slab.h> -+ -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "debug.h" -+#include "stats.h" -+#include "mobhdr.h" -+#include "bcache.h" -+#include "config.h" -+ -+/** -+ * mipv6_append_home_addr - Add Home Address Option -+ * @opt: buffer for Home Address Option -+ * @offset: offset from beginning of @opt -+ * @addr: address for HAO -+ * -+ * Adds a Home Address Option to a packet. Option is stored in -+ * @offset from beginning of @opt. The option is created but the -+ * original source address in IPv6 header is left intact. The source -+ * address will be changed from home address to CoA after the checksum -+ * has been calculated in getfrag. Padding is done automatically, and -+ * @opt must have allocated space for both actual option and pad. -+ * Returns offset from @opt to end of options. -+ **/ -+int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr) -+{ -+ int pad; -+ struct mipv6_dstopt_homeaddr *ho; -+ -+ DEBUG(DBG_DATADUMP, "HAO: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(addr)); -+ -+ pad = (6 - offset) & 7; -+ mipv6_add_pad(opt + offset, pad); -+ -+ ho = (struct mipv6_dstopt_homeaddr *)(opt + offset + pad); -+ ho->type = MIPV6_TLV_HOMEADDR; -+ ho->length = sizeof(*ho) - 2; -+ ipv6_addr_copy(&ho->addr, addr); -+ -+ return offset + pad + sizeof(*ho); -+} -+static inline int check_hao_validity(struct mipv6_dstopt_homeaddr *haopt, -+ u8 *dst1, -+ struct in6_addr *saddr, -+ struct in6_addr *daddr) -+{ -+ int addr_type = ipv6_addr_type(&haopt->addr); -+ struct mipv6_bce bc_entry; -+ -+ if (addr_type & IPV6_ADDR_LINKLOCAL || -+ !(addr_type & IPV6_ADDR_UNICAST)) { -+ DEBUG(DBG_INFO, "HAO with link local or non-unicast HoA, " -+ "not sending BE to " -+ "home address " -+ "%x:%x:%x:%x:%x:%x:%x:%x ", -+ "care-of address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&haopt->addr), -+ NIPV6ADDR(saddr)); -+ return -EINVAL; -+ } else if (dst1[0] != IPPROTO_MOBILITY && -+ (mipv6_bcache_get(&haopt->addr, -+ daddr, &bc_entry) != 0 || -+ ipv6_addr_cmp(saddr, &bc_entry.coa))) { -+ DEBUG(DBG_INFO, "HAO without binding or incorrect CoA, " -+ "sending BE code 1: " -+ "home address %x:%x:%x:%x:%x:%x:%x:%x", -+ "to care-of address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&haopt->addr), -+ NIPV6ADDR(saddr)); -+ return -ENOENT; -+ } -+ return 0; -+} -+/** -+ * mipv6_handle_homeaddr - Home Address Destination Option handler -+ * @skb: packet buffer -+ * @optoff: offset to where option begins -+ * -+ * Handles Home Address Option in IPv6 Destination Option header. -+ * Packet and offset to option are passed. If HAO is used without -+ * binding, sends a Binding Error code 1. When sending BE, notify bit -+ * is cleared to prevent IPv6 error handling from sending ICMP -+ * Parameter Problem. Returns 1 on success, otherwise zero. -+ **/ -+int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff) -+{ -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr coaddr; -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *) skb->cb; -+ struct mipv6_dstopt_homeaddr *haopt = -+ (struct mipv6_dstopt_homeaddr *) &skb->nh.raw[optoff]; -+ u8 *dst1; -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ if (haopt->length != sizeof(*haopt) - 2) { -+ DEBUG(DBG_WARNING, "HAO has invalid length"); -+ MIPV6_INC_STATS(n_ha_drop.invalid); -+ return 0; -+ } -+ dst1 = (u8 *)skb->h.raw; -+ err = check_hao_validity(haopt, dst1, saddr, &skb->nh.ipv6h->daddr); -+ -+ if (err) { -+ haopt->type &= ~(0x80); /* clear notify bit */ -+ if (err == -ENOENT) -+ mipv6_send_be(&skb->nh.ipv6h->daddr, saddr, -+ &haopt->addr, MIPV6_BE_HAO_WO_BINDING); -+ MIPV6_INC_STATS(n_ha_drop.misc); -+ return 0; -+ } -+ ipv6_addr_copy(&coaddr, saddr); -+ ipv6_addr_copy(saddr, &haopt->addr); -+ ipv6_addr_copy(&haopt->addr, &coaddr); -+ opt->hao = optoff; -+ if (mip6_fn.mn_check_tunneled_packet != NULL) -+ mip6_fn.mn_check_tunneled_packet(skb); -+ -+ MIPV6_INC_STATS(n_ha_rcvd); -+ return 1; -+} -+ -+/** -+ * mipv6_icmp_swap_addrs - Switch HAO and src and RT2 and dest for ICMP errors -+ * @skb: packet buffer -+ * -+ * Reset the source address and the Home Address option in skb before -+ * appending it to an ICMP error message, so original packet appears -+ * in the error message rather than mangled. -+ **/ -+void mipv6_icmp_swap_addrs(struct sk_buff *skb) -+{ -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; -+ struct in6_addr tmp; -+ struct in6_addr *hoa; -+ DEBUG_FUNC(); -+ if (opt->srcrt2) { -+ struct rt2_hdr *rt2; -+ rt2 = (struct rt2_hdr *)(skb->nh.raw + opt->srcrt2); -+ hoa = &rt2->addr; -+ -+ ipv6_addr_copy(&tmp, hoa); -+ ipv6_addr_copy(hoa, &skb->nh.ipv6h->daddr); -+ ipv6_addr_copy(&skb->nh.ipv6h->daddr, &tmp); -+ rt2->rt_hdr.segments_left++; -+ skb->nh.ipv6h->hop_limit++; -+ } -+ if (opt->hao) { -+ struct mipv6_dstopt_homeaddr *hao; -+ hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao); -+ hoa = &hao->addr; -+ -+ ipv6_addr_copy(&tmp, hoa); -+ ipv6_addr_copy(hoa, &skb->nh.ipv6h->saddr); -+ ipv6_addr_copy(&skb->nh.ipv6h->saddr, &tmp); -+ } -+} -+ -+/** -+ * mipv6_append_rt2hdr - Add Type 2 Routing Header -+ * @rt: buffer for new routing header -+ * @addr: intermediate hop address -+ * -+ * Adds a Routing Header Type 2 in a packet. Stores newly created -+ * routing header in buffer @rt. Type 2 RT only carries one address, -+ * so there is no need to process old routing header. @rt must have -+ * allocated space for 24 bytes. -+ **/ -+void mipv6_append_rt2hdr(struct ipv6_rt_hdr *rt, struct in6_addr *addr) -+{ -+ struct rt2_hdr *rt2 = (struct rt2_hdr *)rt; -+ -+ DEBUG(DBG_DATADUMP, "RT2: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(addr)); -+ -+ if (ipv6_addr_type(addr) == IPV6_ADDR_MULTICAST) { -+ DEBUG(DBG_ERROR, "destination address not unicast"); -+ return; -+ } -+ -+ memset(rt2, 0, sizeof(*rt2)); -+ rt2->rt_hdr.type = 2; -+ rt2->rt_hdr.hdrlen = 2; -+ rt2->rt_hdr.segments_left = 1; -+ ipv6_addr_copy(&rt2->addr, addr); -+} -+ -+/** -+ * mipv6_append_dst1opts - Add Destination Option (1) Headers -+ * @dst1opt: buffer for new destination options -+ * @saddr: address for Home Address Option -+ * @old_dst1opt: old destination options -+ * @len: length of options -+ * -+ * Adds Destination Option (1) Header to a packet. New options are -+ * stored in @dst1opt. If old destination options exist, they are -+ * copied from @old_dst1opt. Only Home Address Option is destination -+ * option. @dstopt must have allocated space for @len bytes. @len -+ * includes Destination Option Header (2 bytes), Home Address Option -+ * (18 bytes) and possible HAO pad (8n+6). -+ **/ -+/* -+ * ISSUE: Home Address Destination Option should really be added to a -+ * new destination option header specified in Mobile IPv6 spec which -+ * should be placed after routing header(s), but before fragmentation -+ * header. Putting HAO in DO1 works for now, but support for the new -+ * placement should be added to the IPv6 stack. -+ */ -+void -+mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr, -+ struct ipv6_opt_hdr *old_dst1opt, int len) -+{ -+ int offset; -+ -+ if (old_dst1opt) { -+ memcpy(dst1opt, old_dst1opt, ipv6_optlen(old_dst1opt)); -+ offset = ipv6_optlen(old_dst1opt); -+ } else { -+ offset = sizeof (*dst1opt); -+ } -+ dst1opt->hdrlen = (len >> 3) - 1; -+ mipv6_append_home_addr((__u8 *) dst1opt, offset, saddr); -+} -+ -+/** -+ * mipv6_modify_txoptions - Modify outgoing packets -+ * @sk: socket -+ * @skb: packet buffer for outgoing packet -+ * @old_opt: transmit options -+ * @fl: packet flow structure -+ * @dst: pointer to destination cache entry -+ * -+ * Adds Home Address Option (for MN packets, when not at home) and -+ * Routing Header Type 2 (for CN packets when sending to an MN) to -+ * data packets. Old extension headers are copied from @old_opt (if -+ * any). Extension headers are _explicitly_ added for packets with -+ * Mobility Header. Returns the new header structure, or old if no -+ * changes. -+ **/ -+struct ipv6_txoptions * -+mipv6_modify_txoptions(struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *old_opt, struct flowi *fl, -+ struct dst_entry **dst) -+{ -+ struct ipv6_opt_hdr *old_hopopt = NULL; -+ struct ipv6_opt_hdr *old_dst1opt = NULL; -+ struct ipv6_rt_hdr *old_srcrt = NULL; -+ -+ int srcrtlen = 0, dst1len = 0; -+ int tot_len, use_hao = 0; -+ struct ipv6_txoptions *opt; -+ struct mipv6_bce bc_entry; -+ struct in6_addr tmpaddr, *saddr, *daddr, coaddr; -+ __u8 *opt_ptr; -+ -+ DEBUG_FUNC(); -+ -+ if (fl->proto == IPPROTO_MOBILITY) return old_opt; -+ /* -+ * we have to be prepared to the fact that saddr might not be present, -+ * if that is the case, we acquire saddr just as kernel does. -+ */ -+ saddr = fl ? fl->fl6_src : NULL; -+ daddr = fl ? fl->fl6_dst : NULL; -+ -+ if (daddr == NULL) -+ return old_opt; -+ if (saddr == NULL) { -+ int err = ipv6_get_saddr(NULL, daddr, &tmpaddr); -+ if (err) -+ return old_opt; -+ else -+ saddr = &tmpaddr; -+ } -+ -+ DEBUG(DBG_DATADUMP, -+ "dest. address of packet: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(daddr)); -+ DEBUG(DBG_DATADUMP, " and src. address: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(saddr)); -+ -+ if (old_opt) { -+ old_hopopt = old_opt->hopopt; -+ old_dst1opt = old_opt->dst1opt; -+ old_srcrt = old_opt->srcrt; -+ } -+ -+ if (mip6_fn.mn_use_hao != NULL) -+ use_hao = mip6_fn.mn_use_hao(daddr, saddr); -+ -+ if (use_hao) { -+ if (old_dst1opt) -+ dst1len = ipv6_optlen(old_dst1opt); -+ dst1len += sizeof(struct mipv6_dstopt_homeaddr) + -+ ((6 - dst1len) & 7); /* padding */ -+ } -+ -+ if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0) -+ srcrtlen = sizeof(struct rt2_hdr); -+ -+ if ((tot_len = srcrtlen + dst1len) == 0) { -+ return old_opt; -+ } -+ -+ tot_len += sizeof(*opt); -+ -+ if (!(opt = kmalloc(tot_len, GFP_ATOMIC))) { -+ return NULL; -+ } -+ memset(opt, 0, tot_len); -+ opt->tot_len = tot_len; -+ opt_ptr = (__u8 *) (opt + 1); -+ -+ if (old_srcrt) { -+ opt->srcrt = old_srcrt; -+ opt->opt_nflen += ipv6_optlen(old_srcrt); -+ } -+ -+ if (srcrtlen) { -+ DEBUG(DBG_DATADUMP, "Binding exists. Adding routing header"); -+ -+ opt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr; -+ opt->opt_nflen += srcrtlen; -+ opt_ptr += srcrtlen; -+ -+ /* -+ * Append care-of-address to routing header (original -+ * destination address is home address, the first -+ * source route segment gets put to the destination -+ * address and the home address gets to the last -+ * segment of source route (just as it should)) -+ */ -+ -+ ipv6_addr_copy(&coaddr, &bc_entry.coa); -+ -+ mipv6_append_rt2hdr(opt->srcrt2, &coaddr); -+ -+ /* -+ * reroute output (we have to do this in case of TCP -+ * segment) unless a routing header of type 0 is also added -+ */ -+ if (dst && !opt->srcrt) { -+ struct in6_addr *tmp = fl->fl6_dst; -+ fl->fl6_dst = &coaddr; -+ -+ dst_release(*dst); -+ *dst = ip6_route_output(sk, fl); -+ if (skb) -+ skb->dst = *dst; -+ fl->fl6_dst = tmp; -+ -+ DEBUG(DBG_DATADUMP, "Rerouted outgoing packet"); -+ } -+ } -+ -+ /* Only home address option is inserted to first dst opt header */ -+ if (dst1len) { -+ opt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr; -+ opt->opt_flen += dst1len; -+ opt_ptr += dst1len; -+ mipv6_append_dst1opts(opt->dst1opt, saddr, -+ old_dst1opt, dst1len); -+ opt->mipv6_flags = MIPV6_SND_HAO; -+ } else if (old_dst1opt) { -+ opt->dst1opt = old_dst1opt; -+ opt->opt_flen += ipv6_optlen(old_dst1opt); -+ } -+ if (old_hopopt) { -+ opt->hopopt = old_hopopt; -+ opt->opt_nflen += ipv6_optlen(old_hopopt); -+ } -+ -+ return opt; -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/exthdrs.h -@@ -0,0 +1,47 @@ -+/* -+ * MIPL Mobile IPv6 Extension Headers header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MIPV6_EXTHDRS_H -+#define _MIPV6_EXTHDRS_H -+ -+struct in6_addr; -+struct sk_buff; -+struct ipv6_rt_hdr; -+struct ipv6_opt_hdr; -+struct ipv6_txoptions; -+struct flowi; -+struct dst_entry; -+/* -+ * Home Address Destination Option function prototypes -+ */ -+int mipv6_append_home_addr(__u8 *opt, int offset, struct in6_addr *addr); -+ -+int mipv6_handle_homeaddr(struct sk_buff *skb, int optoff); -+ -+void mipv6_icmp_swap_addrs(struct sk_buff *skb); -+ -+/* -+ * Creates a routing header of type 2. -+ */ -+void mipv6_append_rt2hdr(struct ipv6_rt_hdr *srcrt, struct in6_addr *addr); -+ -+/* Function to add the first destination option header, which may -+ * include a home address option. -+ */ -+void mipv6_append_dst1opts(struct ipv6_opt_hdr *dst1opt, struct in6_addr *saddr, -+ struct ipv6_opt_hdr *old_dst1opt, int len); -+ -+struct ipv6_txoptions *mipv6_modify_txoptions( -+ struct sock *sk, struct sk_buff *skb, -+ struct ipv6_txoptions *old_opt, struct flowi *fl, -+ struct dst_entry **dst); -+ -+#endif /* _MIPV6_EXTHDRS_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/ha.c -@@ -0,0 +1,553 @@ -+/* -+ * Home-agent functionality -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Henrik Petander <lpetande@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Changes: Venkata Jagana, -+ * Krishna Kumar : Statistics fix -+ * Masahide Nakamura : Use of mipv6_forward -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/net.h> -+#include <linux/skbuff.h> -+#include <linux/if_ether.h> -+#include <linux/netdevice.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/netfilter.h> -+#include <linux/netfilter_ipv6.h> -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif -+ -+#include <net/neighbour.h> -+#include <net/ipv6.h> -+#include <net/ip6_fib.h> -+#include <net/ip6_route.h> -+#include <net/ndisc.h> -+#include <net/addrconf.h> -+#include <net/neighbour.h> -+ -+#include "tunnel_ha.h" -+#include "bcache.h" -+#include "stats.h" -+#include "debug.h" -+#include "util.h" -+#include "ha.h" -+#include "config.h" -+#include "mobhdr.h" -+ -+static int mipv6_ha_tunnel_sitelocal = 0; -+ -+#ifdef CONFIG_SYSCTL -+ -+static struct ctl_table_header *mipv6_ha_sysctl_header; -+ -+static struct mipv6_ha_sysctl_table -+{ -+ struct ctl_table_header *sysctl_header; -+ ctl_table mipv6_vars[3]; -+ ctl_table mipv6_mobility_table[2]; -+ ctl_table mipv6_proto_table[2]; -+ ctl_table mipv6_root_table[2]; -+} mipv6_ha_sysctl = { -+ NULL, -+ -+ {{NET_IPV6_MOBILITY_TUNNEL_SITELOCAL, "tunnel_sitelocal", -+ &mipv6_ha_tunnel_sitelocal, sizeof(int), 0644, NULL, -+ &proc_dointvec}, -+ {0}}, -+ -+ {{NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, -+ mipv6_ha_sysctl.mipv6_vars}, {0}}, -+ {{NET_IPV6, "ipv6", NULL, 0, 0555, -+ mipv6_ha_sysctl.mipv6_mobility_table}, {0}}, -+ {{CTL_NET, "net", NULL, 0, 0555, -+ mipv6_ha_sysctl.mipv6_proto_table}, {0}} -+}; -+ -+#endif /* CONFIG_SYSCTL */ -+ -+ -+/* this is defined in kernel IPv6 module (sockglue.c) */ -+extern struct packet_type ipv6_packet_type; -+ -+/* mipv6_forward: Intercept NS packets destined to home address of MN */ -+int mipv6_forward(struct sk_buff *skb) -+{ -+ struct ipv6hdr *ipv6h; -+ struct in6_addr *daddr, *saddr; -+ __u8 nexthdr; -+ int nhoff; -+ -+ if (skb == NULL) return 0; -+ -+ ipv6h = skb->nh.ipv6h; -+ daddr = &ipv6h->daddr; -+ saddr = &ipv6h->saddr; -+ -+ nexthdr = ipv6h->nexthdr; -+ nhoff = sizeof(*ipv6h); -+ -+ if (ipv6_ext_hdr(nexthdr)) -+ nhoff = ipv6_skip_exthdr(skb, nhoff, &nexthdr, -+ skb->len - sizeof(*ipv6h)); -+ -+ /* Do not to forward Neighbor Solicitation to Home Address of MN */ -+ if (nexthdr == IPPROTO_ICMPV6) { -+ struct icmp6hdr *icmp6h; -+ int dest_type; -+ -+ if (nhoff < 0 || !pskb_may_pull(skb, nhoff + -+ sizeof(struct icmp6hdr))) { -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ dest_type = ipv6_addr_type(daddr); -+ icmp6h = (struct icmp6hdr *)&skb->nh.raw[nhoff]; -+ -+ /* Intercepts NS to HoA of MN */ -+ -+ if ((icmp6h->icmp6_type == NDISC_NEIGHBOUR_SOLICITATION) || -+ ((dest_type & IPV6_ADDR_MULTICAST) && -+ (icmp6h->icmp6_type == NDISC_ROUTER_ADVERTISEMENT))) { -+ ip6_input(skb); -+ } else { -+ ip6_forward(skb); -+ } -+ } else { -+ ip6_forward(skb); -+ } -+ return 0; -+} -+ -+ -+/** -+ * mipv6_proxy_nd_rem - stop acting as a proxy for @home_address -+ * @home_addr: address to remove -+ * @ha_addr: home agent's address on home link -+ * @linklocal: link-local compatibility bit -+ * -+ * When Home Agent acts as a proxy for an address it must leave the -+ * solicited node multicast group for that address and stop responding -+ * to neighbour solicitations. -+ **/ -+static int mipv6_proxy_nd_rem(struct in6_addr *home_addr, -+ int ifindex, int linklocal) -+{ -+ /* When MN returns home HA leaves the solicited mcast groups -+ * for MNs home addresses -+ */ -+ int err; -+ struct net_device *dev; -+ -+ DEBUG_FUNC(); -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) { -+ DEBUG(DBG_ERROR, "couldn't get dev"); -+ return -ENODEV; -+ } -+#if 1 /* TEST */ -+ /* Remove link-local entry */ -+ if (linklocal) { -+ struct in6_addr ll_addr; -+ mipv6_generate_ll_addr(&ll_addr, home_addr); -+ if ((err = pneigh_delete(&nd_tbl, &ll_addr, dev)) < 0) { -+ DEBUG(DBG_INFO, -+ "peigh_delete failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&ll_addr)); -+ } -+ } -+#endif -+ /* Remove global (or site-local) entry */ -+ if ((err = pneigh_delete(&nd_tbl, home_addr, dev)) < 0) { -+ DEBUG(DBG_INFO, -+ "peigh_delete failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(home_addr)); -+ } -+ dev_put(dev); -+ return err; -+} -+ -+/** -+ * mipv6_proxy_nd - join multicast group for this address -+ * @home_addr: address to defend -+ * @ha_addr: home agent's address on home link -+ * @linklocal: link-local compatibility bit -+ * -+ * While Mobile Node is away from home, Home Agent acts as a proxy for -+ * @home_address. HA responds to neighbour solicitations for @home_address -+ * thus getting all packets destined to home address of MN. -+ **/ -+static int mipv6_proxy_nd(struct in6_addr *home_addr, -+ int ifindex, int linklocal) -+{ -+ /* The HA sends a proxy ndisc_na message to all hosts on MN's -+ * home subnet by sending a neighbor advertisement with the -+ * home address or all addresses of the mobile node if the -+ * prefix is not 0. The addresses are formed by combining the -+ * suffix or the host part of the address with each subnet -+ * prefix that exists in the home subnet -+ */ -+ -+ /* Since no previous entry for MN exists a proxy_nd advertisement -+ * is sent to all nodes link local multicast address -+ */ -+ int err = -1; -+ -+ struct net_device *dev; -+ struct in6_addr na_saddr; -+ struct in6_addr ll_addr; -+ struct pneigh_entry *ll_pneigh; -+ struct in6_addr mcdest; -+ int send_ll_na = 0; -+ int inc_opt = 1; -+ int solicited = 0; -+ int override = 1; -+ -+ DEBUG_FUNC(); -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) { -+ DEBUG(DBG_ERROR, "couldn't get dev"); -+ return -ENODEV; -+ } -+ -+ if (!pneigh_lookup(&nd_tbl, home_addr, dev, 1)) { -+ DEBUG(DBG_INFO, -+ "peigh_lookup failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(home_addr)); -+ goto free_dev; -+ } -+#if 1 /* TEST */ -+ if (linklocal) { -+ mipv6_generate_ll_addr(&ll_addr, home_addr); -+ -+ if ((ll_pneigh = pneigh_lookup(&nd_tbl, &ll_addr, -+ dev, 1)) == NULL) { -+ DEBUG(DBG_INFO, -+ "peigh_lookup failed for " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&ll_addr)); -+ pneigh_delete(&nd_tbl, home_addr, dev); -+ goto free_dev; -+ } else { -+ send_ll_na = 1; -+ } -+ } else { -+ ll_pneigh = NULL; -+ } -+#endif -+ /* Proxy neighbor advertisement of MN's home address -+ * to all nodes solicited multicast address -+ */ -+ if (!ipv6_get_lladdr(dev, &na_saddr)) { -+ ipv6_addr_all_nodes(&mcdest); -+ ndisc_send_na(dev, NULL, &mcdest, home_addr, 0, -+ solicited, override, inc_opt); -+#if 1 /* TEST */ -+ if (send_ll_na) { -+ ndisc_send_na(dev, NULL, &mcdest, &ll_addr, -+ 0, solicited, override, inc_opt); -+ } -+#endif -+ err = 0; -+ } else { -+ DEBUG(DBG_ERROR, "failed to get link local address for sending proxy NA"); -+ } -+free_dev: -+ dev_put(dev); -+ return err; -+ -+} -+ -+struct inet6_ifaddr *is_on_link_ipv6_address(struct in6_addr *mn_haddr, -+ struct in6_addr *ha_addr) -+{ -+ struct inet6_ifaddr *ifp; -+ struct inet6_dev *in6_dev; -+ struct inet6_ifaddr *oifp = NULL; -+ -+ if ((ifp = ipv6_get_ifaddr(ha_addr, 0)) == NULL) -+ return NULL; -+ -+ if ((in6_dev = ifp->idev) != NULL) { -+ in6_dev_hold(in6_dev); -+ oifp = in6_dev->addr_list; -+ while (oifp != NULL) { -+ spin_lock(&oifp->lock); -+ if (mipv6_prefix_compare(&oifp->addr, mn_haddr, -+ oifp->prefix_len) && -+ !(oifp->flags & IFA_F_TENTATIVE)) { -+ spin_unlock(&oifp->lock); -+ DEBUG(DBG_INFO, "Home Addr Opt: on-link"); -+ in6_ifa_hold(oifp); -+ break; -+ } -+ spin_unlock(&oifp->lock); -+ oifp = oifp->if_next; -+ } -+ in6_dev_put(in6_dev); -+ } -+ in6_ifa_put(ifp); -+/* DEBUG(DBG_WARNING, "Home Addr Opt NOT on-link"); */ -+ return oifp; -+ -+} -+ -+/* -+ * Lifetime checks. ifp->valid_lft >= ifp->prefered_lft always (see addrconf.c) -+ * Returned value is in seconds. -+ */ -+ -+static __u32 get_min_lifetime(struct inet6_ifaddr *ifp, __u32 lifetime) -+{ -+ __u32 rem_lifetime = 0; -+ unsigned long now = jiffies; -+ -+ if (ifp->valid_lft == 0) { -+ rem_lifetime = lifetime; -+ } else { -+ __u32 valid_lft_left = -+ ifp->valid_lft - ((now - ifp->tstamp) / HZ); -+ rem_lifetime = -+ min_t(unsigned long, valid_lft_left, lifetime); -+ } -+ -+ return rem_lifetime; -+} -+ -+#define MAX_LIFETIME 1000 -+ -+/** -+ * mipv6_lifetime_check - check maximum lifetime is not exceeded -+ * @lifetime: lifetime to check -+ * -+ * Checks @lifetime does not exceed %MAX_LIFETIME. Returns @lifetime -+ * if not exceeded, otherwise returns %MAX_LIFETIME. -+ **/ -+static int mipv6_lifetime_check(int lifetime) -+{ -+ return (lifetime > MAX_LIFETIME) ? MAX_LIFETIME : lifetime; -+} -+ -+/* Generic routine handling finish of BU processing */ -+void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, __u8 ba_status, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 ba_lifetime, __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ int err; -+ -+ if (ba_status >= REASON_UNSPECIFIED) { -+ /* DAD failed */ -+ goto out; -+ } -+ -+ ba_lifetime = get_min_lifetime(ifp, ba_lifetime); -+ ba_lifetime = mipv6_lifetime_check(ba_lifetime); -+ -+ if ((err = mipv6_bcache_add(ifindex, daddr, haddr, coa, -+ ba_lifetime, sequence, flags, -+ HOME_REGISTRATION)) != 0 ) { -+ DEBUG(DBG_WARNING, "home reg failed."); -+ -+ if (err == -ENOMEDIUM) -+ return; -+ -+ ba_status = INSUFFICIENT_RESOURCES; -+ } else { -+ DEBUG(DBG_INFO, "home reg succeeded."); -+ } -+ -+ DEBUG(DBG_DATADUMP, "home_addr: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(haddr)); -+ DEBUG(DBG_DATADUMP, "coa: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(coa)); -+ DEBUG(DBG_DATADUMP, "lifet:%d, seq:%d", ba_lifetime, sequence); -+out: -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence, -+ ba_lifetime, k_bu); -+} -+ -+static int ha_proxy_create(int flags, int ifindex, struct in6_addr *coa, -+ struct in6_addr *our_addr, struct in6_addr *home_addr) -+{ -+ int ret; -+ -+ if ((ret = mipv6_add_tnl_to_mn(coa, our_addr, home_addr)) <= 0) { -+ if (ret != -ENOMEDIUM) { -+ DEBUG(DBG_ERROR, "unable to configure tunnel to MN!"); -+ } -+ return -1; -+ } -+ if (mipv6_proxy_nd(home_addr, ifindex, -+ flags & MIPV6_BU_F_LLADDR) != 0) { -+ DEBUG(DBG_ERROR, "mipv6_proxy_nd failed!"); -+ mipv6_del_tnl_to_mn(coa, our_addr, home_addr); -+ return -2; -+ } -+ return 0; -+} -+ -+static void ha_proxy_del(struct in6_addr *home_addr, struct mipv6_bce *entry) -+{ -+ if (mipv6_proxy_nd_rem(&entry->home_addr, entry->ifindex, -+ entry->flags & MIPV6_BU_F_LLADDR) == 0) { -+ DEBUG(DBG_INFO, "proxy_nd succ"); -+ } else { -+ DEBUG(DBG_INFO, "proxy_nd fail"); -+ } -+ mipv6_del_tnl_to_mn(&entry->coa, &entry->our_addr, home_addr); -+} -+ -+static void bc_home_add(int ifindex, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 lifetime, __u16 sequence, __u8 flags, -+ __u8 *k_bu) -+{ -+ struct inet6_ifaddr *ifp = NULL; -+ __u8 ba_status = SUCCESS; -+ -+ DEBUG_FUNC(); -+ -+ ifp = is_on_link_ipv6_address(haddr, daddr); -+ -+ if (ifp == NULL) { -+ ba_status = NOT_HOME_SUBNET; -+ } else if (((ipv6_addr_type(haddr) & IPV6_ADDR_SITELOCAL) || -+ (ipv6_addr_type(coa) & IPV6_ADDR_SITELOCAL)) -+ && !mipv6_ha_tunnel_sitelocal) { -+ /* Site-local home or care-of addresses are not -+ accepted by default */ -+ ba_status = ADMINISTRATIVELY_PROHIBITED; -+ } else { -+ int ret; -+ -+ ifindex = ifp->idev->dev->ifindex; -+ -+ if ((ret = mipv6_dad_start(ifp, ifindex, daddr, -+ haddr, coa, rep_coa, lifetime, -+ sequence, flags)) < 0) { -+ /* An error occurred */ -+ ba_status = -ret; -+ } else if (ret) { -+ /* DAD is needed to be performed. */ -+ in6_ifa_put(ifp); -+ return; -+ } -+ } -+ -+ mipv6_bu_finish(ifp, ifindex, ba_status, daddr, haddr, coa, -+ rep_coa, lifetime, sequence, flags, k_bu); -+ if (ifp) -+ in6_ifa_put(ifp); -+} -+ -+static void bc_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ __u8 status = SUCCESS; -+ struct mipv6_bce bce; -+ -+ /* Primary Care-of Address Deregistration */ -+ if (mipv6_bcache_get(haddr, daddr, &bce) < 0) { -+ DEBUG(DBG_INFO, "entry is not in cache"); -+ status = NOT_HA_FOR_MN; -+ } else { -+ ha_proxy_del(&bce.home_addr, &bce); -+ mipv6_bcache_delete(haddr, daddr, HOME_REGISTRATION); -+ } -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, 0, k_bu); -+} -+ -+extern int mipv6_ra_rcv_ptr(struct sk_buff *skb, struct icmp6hdr *msg); -+ -+ -+static int -+mipv6_ha_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_encapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_ha_tnl_xmit_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_ENCAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_ha_tnl_xmit_stats_hook -+}; -+ -+static int -+mipv6_ha_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_decapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_ha_tnl_rcv_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_DECAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_ha_tnl_rcv_stats_hook -+}; -+ -+static struct mip6_func old; -+ -+int __init mipv6_ha_init(void) -+{ -+ DEBUG_FUNC(); -+ -+#ifdef CONFIG_SYSCTL -+ if (!(mipv6_ha_sysctl_header = -+ register_sysctl_table(mipv6_ha_sysctl.mipv6_root_table, 0))) -+ printk(KERN_ERR "Failed to register sysctl handlers!"); -+#endif -+ memcpy(&old, &mip6_fn, sizeof(struct mip6_func)); -+ mip6_fn.bce_home_add = bc_home_add; -+ mip6_fn.bce_home_del = bc_home_delete; -+ mip6_fn.proxy_del = ha_proxy_del; -+ mip6_fn.proxy_create = ha_proxy_create; -+ /* register packet interception hooks */ -+ ip6ip6_tnl_register_hook(&mipv6_ha_tnl_xmit_stats_ops); -+ ip6ip6_tnl_register_hook(&mipv6_ha_tnl_rcv_stats_ops); -+ return 0; -+} -+ -+void __exit mipv6_ha_exit(void) -+{ -+ DEBUG_FUNC(); -+ -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_ha_sysctl_header); -+#endif -+ -+ /* remove packet interception hooks */ -+ ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_rcv_stats_ops); -+ ip6ip6_tnl_unregister_hook(&mipv6_ha_tnl_xmit_stats_ops); -+ -+ mip6_fn.bce_home_add = old.bce_home_add; -+ mip6_fn.bce_home_del = old.bce_home_del; -+ mip6_fn.proxy_del = old.proxy_del; -+ mip6_fn.proxy_create = old.proxy_create; -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/ha.h -@@ -0,0 +1,39 @@ -+/* -+ * MIPL Mobile IPv6 Home Agent header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HA_H -+#define _HA_H -+ -+int mipv6_ha_init(void); -+void mipv6_ha_exit(void); -+ -+int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 ba_lifetime, __u16 sequence, __u8 flags); -+ -+void mipv6_bu_finish(struct inet6_ifaddr *ifp, int ifindex, -+ __u8 ba_status, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 ba_lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu); -+ -+ -+static __inline__ void mipv6_generate_ll_addr(struct in6_addr *ll_addr, -+ struct in6_addr *addr) -+{ -+ ll_addr->s6_addr32[0] = htonl(0xfe800000); -+ ll_addr->s6_addr32[1] = 0; -+ ll_addr->s6_addr32[2] = addr->s6_addr32[2]; -+ ll_addr->s6_addr32[3] = addr->s6_addr32[3]; -+} -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/halist.c -@@ -0,0 +1,507 @@ -+/* -+ * Home Agents List -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#define PREF_BASE 0xffff /* MAX value for u16 field in RA */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/proc_fs.h> -+#include <linux/init.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+ -+#include "hashlist.h" -+#include "util.h" -+#include "debug.h" -+ -+struct mipv6_halist { -+ struct hashlist *entries; -+ struct timer_list expire_timer; -+}; -+ -+static rwlock_t home_agents_lock = RW_LOCK_UNLOCKED; -+ -+static struct mipv6_halist home_agents; -+ -+struct mipv6_halist_entry { -+ struct hashlist_entry e; -+ int ifindex; /* Link identifier */ -+ struct in6_addr link_local_addr; /* HA's link-local address */ -+ struct in6_addr global_addr; /* HA's Global address */ -+ int plen; -+ long preference; /* The preference for this HA */ -+ unsigned long expire; /* expiration time (jiffies) */ -+}; -+ -+static inline void mipv6_ha_ac_add(struct in6_addr *ll_addr, int ifindex, -+ struct in6_addr *glob_addr, int plen) -+{ -+ struct net_device *dev; -+ -+ if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) { -+ struct in6_addr addr; -+ mipv6_ha_anycast(&addr, glob_addr, plen); -+ ipv6_dev_ac_inc(dev, &addr); -+ } -+} -+ -+static inline void mipv6_ha_ac_del(struct in6_addr *ll_addr, int ifindex, -+ struct in6_addr *glob_addr, int plen) -+{ -+ struct net_device *dev; -+ -+ if ((dev = __dev_get_by_index(ifindex)) && ipv6_chk_addr(ll_addr, dev)) { -+ struct in6_addr addr; -+ mipv6_ha_anycast(&addr, glob_addr, plen); -+ ipv6_dev_ac_dec(dev, &addr); -+ } -+} -+ -+struct preflist_iterator_args { -+ int count; -+ int requested; -+ int ifindex; -+ struct in6_addr *list; -+}; -+ -+static int preflist_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct preflist_iterator_args *state = -+ (struct preflist_iterator_args *)args; -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ struct in6_addr *newaddr = -+ (struct in6_addr *)state->list + state->count; -+ -+ if (state->count >= state->requested) -+ return ITERATOR_STOP; -+ -+ if (time_after(jiffies, entry->expire)) { -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, -+ entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ DEBUG(DBG_INFO, "preflist_iterator: Deleting entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr)); -+ return ITERATOR_DELETE_ENTRY; -+ } -+ if (state->ifindex != entry->ifindex) -+ return ITERATOR_CONT; -+ -+ ipv6_addr_copy(newaddr, &entry->global_addr); -+ DEBUG(DBG_INFO, "preflist_iterator: adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x to list", NIPV6ADDR(&entry->global_addr)); -+ state->count++; -+ -+ return ITERATOR_CONT; -+} -+ -+static int gc_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ -+ int *type = (int *)args; -+ -+ if (*type == 1 || time_after(jiffies, entry->expire)) { -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, -+ entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ return ITERATOR_DELETE_ENTRY; -+ } -+ -+ return ITERATOR_CONT; -+} -+ -+static int mipv6_halist_gc(int type) -+{ -+ DEBUG_FUNC(); -+ hashlist_iterate(home_agents.entries, &type, gc_iterator); -+ return 0; -+} -+ -+static void mipv6_halist_expire(unsigned long dummy) -+{ -+ DEBUG_FUNC(); -+ -+ write_lock(&home_agents_lock); -+ mipv6_halist_gc(0); -+ write_unlock(&home_agents_lock); -+} -+ -+ -+static struct mipv6_halist_entry *mipv6_halist_new_entry(void) -+{ -+ struct mipv6_halist_entry *entry; -+ -+ DEBUG_FUNC(); -+ -+ entry = hashlist_alloc(home_agents.entries, SLAB_ATOMIC); -+ -+ return entry; -+} -+ -+ -+ -+/** -+ * mipv6_halist_add - Add new home agent to the Home Agents List -+ * @ifindex: interface identifier -+ * @glob_addr: home agent's global address -+ * @ll_addr: home agent's link-local address -+ * @pref: relative preference for this home agent -+ * @lifetime: lifetime for the entry -+ * -+ * Adds new home agent to the Home Agents List. The list is interface -+ * specific and @ifindex tells through which interface the home agent -+ * was heard. Returns zero on success and negative on failure. -+ **/ -+ -+int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen, -+ struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime) -+{ -+ int update = 0, ret = 0; -+ unsigned int mpref; -+ struct mipv6_halist_entry *entry = NULL; -+ -+ DEBUG_FUNC(); -+ -+ write_lock(&home_agents_lock); -+ -+ if (glob_addr == NULL || lifetime <= 0) { -+ DEBUG(DBG_WARNING, "invalid arguments"); -+ ret = -EINVAL; -+ goto out; -+ } -+ mpref = PREF_BASE - pref; -+ if ((entry = (struct mipv6_halist_entry *) -+ hashlist_get(home_agents.entries, glob_addr)) != NULL) { -+ if (entry->ifindex == ifindex) { -+ DEBUG(DBG_DATADUMP, "updating old entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr)); -+ update = 1; -+ } else { -+ DEBUG(DBG_INFO, "halist_add : adding new entry with address %x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(glob_addr)); -+ update = 0; -+ } -+ } -+ if (update) { -+ entry->expire = jiffies + lifetime * HZ; -+ if (entry->preference != mpref) { -+ entry->preference = mpref; -+ ret = hashlist_reposition(home_agents.entries, -+ (void *)entry, mpref); -+ } -+ } else { -+ entry = mipv6_halist_new_entry(); -+ if (entry == NULL) { -+ DEBUG(DBG_INFO, "list full"); -+ ret = -ENOMEM; -+ goto out; -+ } -+ entry->ifindex = ifindex; -+ if (ll_addr) { -+ ipv6_addr_copy(&entry->link_local_addr, ll_addr); -+ mipv6_ha_ac_add(ll_addr, ifindex, glob_addr, plen); -+ } else -+ ipv6_addr_set(&entry->link_local_addr, 0, 0, 0, 0); -+ -+ ipv6_addr_copy(&entry->global_addr, glob_addr); -+ entry->plen = plen; -+ entry->preference = mpref; -+ entry->expire = jiffies + lifetime * HZ; -+ ret = hashlist_add(home_agents.entries, glob_addr, mpref, -+ entry); -+ } -+out: -+ write_unlock(&home_agents_lock); -+ return ret; -+} -+ -+/** -+ * mipv6_halist_delete - delete home agent from Home Agents List -+ * @glob_addr: home agent's global address -+ * -+ * Deletes entry for home agent @glob_addr from the Home Agent List. -+ **/ -+int mipv6_halist_delete(struct in6_addr *glob_addr) -+{ -+ struct hashlist_entry *e; -+ struct mipv6_halist_entry *entry; -+ DEBUG_FUNC(); -+ -+ if (glob_addr == NULL) { -+ DEBUG(DBG_WARNING, "invalid glob addr"); -+ return -EINVAL; -+ } -+ write_lock(&home_agents_lock); -+ if ((e = hashlist_get(home_agents.entries, glob_addr)) == NULL) { -+ write_unlock(&home_agents_lock); -+ return -ENOENT; -+ } -+ hashlist_delete(home_agents.entries, e); -+ entry = (struct mipv6_halist_entry *)e; -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ hashlist_free(home_agents.entries, e); -+ write_unlock(&home_agents_lock); -+ return 0; -+} -+ -+/** -+ * mipv6_ha_get_pref_list - Get list of preferred home agents -+ * @ifindex: interface identifier -+ * @addrs: pointer to a buffer to store the list -+ * @max: maximum number of home agents to return -+ * -+ * Creates a list of @max preferred (or all known if less than @max) -+ * home agents. Home Agents List is interface specific so you must -+ * supply @ifindex. Stores list in addrs and returns number of home -+ * agents stored. On failure, returns a negative value. -+ **/ -+int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max) -+{ -+ struct preflist_iterator_args args; -+ -+ DEBUG_FUNC(); -+ if (max <= 0) { -+ *addrs = NULL; -+ return 0; -+ } -+ -+ args.count = 0; -+ args.requested = max; -+ args.ifindex = ifindex; -+ args.list = kmalloc(max * sizeof(struct in6_addr), GFP_ATOMIC); -+ -+ if (args.list == NULL) return -ENOMEM; -+ -+ read_lock(&home_agents_lock); -+ hashlist_iterate(home_agents.entries, &args, preflist_iterator); -+ read_unlock(&home_agents_lock); -+ -+ if (args.count >= 0) { -+ *addrs = args.list; -+ } else { -+ kfree(args.list); -+ *addrs = NULL; -+ } -+ -+ return args.count; -+} -+ -+struct getaddr_iterator_args { -+ struct net_device *dev; -+ struct in6_addr *addr; -+}; -+ -+static int getaddr_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ struct getaddr_iterator_args *state = -+ (struct getaddr_iterator_args *)args; -+ -+ if (entry->ifindex != state->dev->ifindex) -+ return ITERATOR_CONT; -+ -+ if (ipv6_chk_addr(&entry->global_addr, state->dev)) { -+ ipv6_addr_copy(state->addr, &entry->global_addr); -+ return ITERATOR_STOP; -+ } -+ return ITERATOR_CONT; -+} -+ -+/* -+ * Get Home Agent Address for given interface. If node is not serving -+ * as a HA for this interface returns negative error value. -+ */ -+int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr) -+{ -+ struct getaddr_iterator_args args; -+ struct net_device *dev; -+ -+ if (ifindex <= 0) -+ return -EINVAL; -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) -+ return -ENODEV; -+ -+ memset(addr, 0, sizeof(struct in6_addr)); -+ args.dev = dev; -+ args.addr = addr; -+ read_lock(&home_agents_lock); -+ hashlist_iterate(home_agents.entries, &args, getaddr_iterator); -+ read_unlock(&home_agents_lock); -+ dev_put(dev); -+ -+ if (ipv6_addr_any(addr)) -+ return -ENOENT; -+ -+ return 0; -+} -+ -+#define HALIST_INFO_LEN 81 -+ -+struct procinfo_iterator_args { -+ char *buffer; -+ int offset; -+ int length; -+ int skip; -+ int len; -+}; -+ -+static int procinfo_iterator(void *data, void *args, -+ unsigned long *pref) -+{ -+ struct procinfo_iterator_args *arg = -+ (struct procinfo_iterator_args *)args; -+ struct mipv6_halist_entry *entry = -+ (struct mipv6_halist_entry *)data; -+ unsigned long int expire; -+ -+ DEBUG_FUNC(); -+ -+ if (entry == NULL) return ITERATOR_ERR; -+ -+ if (time_after(jiffies, entry->expire)) { -+ if (!ipv6_addr_any(&entry->link_local_addr)) { -+ mipv6_ha_ac_del(&entry->link_local_addr, -+ entry->ifindex, -+ &entry->global_addr, entry->plen); -+ } -+ return ITERATOR_DELETE_ENTRY; -+ } -+ if (arg->skip < arg->offset / HALIST_INFO_LEN) { -+ arg->skip++; -+ return ITERATOR_CONT; -+ } -+ -+ if (arg->len >= arg->length) -+ return ITERATOR_CONT; -+ -+ expire = (entry->expire - jiffies) / HZ; -+ -+ arg->len += sprintf(arg->buffer + arg->len, -+ "%02d %08x%08x%08x%08x %08x%08x%08x%08x %05ld %05ld\n", -+ entry->ifindex, -+ ntohl(entry->global_addr.s6_addr32[0]), -+ ntohl(entry->global_addr.s6_addr32[1]), -+ ntohl(entry->global_addr.s6_addr32[2]), -+ ntohl(entry->global_addr.s6_addr32[3]), -+ ntohl(entry->link_local_addr.s6_addr32[0]), -+ ntohl(entry->link_local_addr.s6_addr32[1]), -+ ntohl(entry->link_local_addr.s6_addr32[2]), -+ ntohl(entry->link_local_addr.s6_addr32[3]), -+ -(entry->preference - PREF_BASE), expire); -+ -+ return ITERATOR_CONT; -+} -+ -+static int halist_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct procinfo_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.buffer = buffer; -+ args.offset = offset; -+ args.length = length; -+ args.skip = 0; -+ args.len = 0; -+ -+ read_lock_bh(&home_agents_lock); -+ hashlist_iterate(home_agents.entries, &args, procinfo_iterator); -+ read_unlock_bh(&home_agents_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % HALIST_INFO_LEN; -+ -+ args.len -= offset % HALIST_INFO_LEN; -+ -+ if (args.len > length) -+ args.len = length; -+ if (args.len < 0) -+ args.len = 0; -+ -+ return args.len; -+} -+ -+static int halist_compare(void *data, void *hashkey) -+{ -+ struct mipv6_halist_entry *e = (struct mipv6_halist_entry *)data; -+ struct in6_addr *key = (struct in6_addr *)hashkey; -+ -+ return ipv6_addr_cmp(&e->global_addr, key); -+} -+ -+static __u32 halist_hash(void *hashkey) -+{ -+ struct in6_addr *key = (struct in6_addr *)hashkey; -+ __u32 hash; -+ -+ hash = key->s6_addr32[0] ^ -+ key->s6_addr32[1] ^ -+ key->s6_addr32[2] ^ -+ key->s6_addr32[3]; -+ -+ return hash; -+} -+ -+int __init mipv6_halist_init(__u32 size) -+{ -+ DEBUG_FUNC(); -+ -+ if (size <= 0) { -+ DEBUG(DBG_ERROR, "size must be at least 1"); -+ return -EINVAL; -+ } -+ init_timer(&home_agents.expire_timer); -+ home_agents.expire_timer.data = 0; -+ home_agents.expire_timer.function = mipv6_halist_expire; -+ home_agents_lock = RW_LOCK_UNLOCKED; -+ -+ home_agents.entries = hashlist_create(16, size, sizeof(struct mipv6_halist_entry), -+ "mip6_halist", NULL, NULL, -+ halist_compare, halist_hash); -+ -+ if (home_agents.entries == NULL) { -+ DEBUG(DBG_ERROR, "Failed to initialize hashlist"); -+ return -ENOMEM; -+ } -+ -+ proc_net_create("mip6_home_agents", 0, halist_proc_info); -+ DEBUG(DBG_INFO, "Home Agents List initialized"); -+ return 0; -+} -+ -+void __exit mipv6_halist_exit(void) -+{ -+ DEBUG_FUNC(); -+ proc_net_remove("mip6_home_agents"); -+ write_lock_bh(&home_agents_lock); -+ DEBUG(DBG_INFO, "Stopping the halist timer"); -+ del_timer(&home_agents.expire_timer); -+ mipv6_halist_gc(1); -+ write_unlock_bh(&home_agents_lock); -+ hashlist_destroy(home_agents.entries); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/halist.h -@@ -0,0 +1,28 @@ -+/* -+ * MIPL Mobile IPv6 Home Agents List header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HALIST_H -+#define _HALIST_H -+ -+int mipv6_halist_init(__u32 size); -+ -+void mipv6_halist_exit(void); -+ -+int mipv6_halist_add(int ifindex, struct in6_addr *glob_addr, int plen, -+ struct in6_addr *ll_addr, unsigned int pref, __u32 lifetime); -+ -+int mipv6_halist_delete(struct in6_addr *glob_addr); -+ -+int mipv6_ha_get_pref_list(int ifindex, struct in6_addr **addrs, int max); -+ -+int mipv6_ha_get_addr(int ifindex, struct in6_addr *addr); -+ -+#endif /* _HALIST_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/hashlist.c -@@ -0,0 +1,351 @@ -+/* -+ * Generic hashtable with chaining. Supports secodary sort order -+ * with doubly linked-list. -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id: s.hashlist.c 1.21 02/10/07 19:31:52+03:00 antti@traci.mipl.mediapoli.com $ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ */ -+ -+#include <linux/slab.h> -+#include "hashlist.h" -+#include "debug.h" -+ -+struct hashlist { -+ int count; /* entry count */ -+ int maxcount; /* max entries */ -+ __u32 bucketnum; /* hash buckets */ -+ -+ kmem_cache_t *kmem; -+ -+ struct list_head *hashtable; -+ struct list_head sortedlist; -+ -+ int (*compare)(void *data, void *hashkey); -+ __u32 (*hash_function)(void *hashkey); -+}; -+ -+/** -+ * hashlist_create - Create new hashlist -+ * @bucketnum: number of hash buckets -+ * @maxentries: maximum number of entries (0 = no limit) -+ * @size: entry size in bytes -+ * @name: name for kmem_cache_t -+ * @ctor: kmem_cache_t constructor -+ * @dtor: kmem_cache_t destructor -+ * @compare: compare function for key -+ * @hash_function: hash function -+ * -+ * Creates a hashlist structure with @max_entries entries of @size -+ * bytes. User must supply @hash_function and @compare function for -+ * the hashlist. User can also supply @ctor and @dtor for kmem_cache. -+ **/ -+struct hashlist *hashlist_create(int bucketnum, int max_entries, size_t size, -+ char *name, -+ void (*ctor)(void *, kmem_cache_t *, unsigned long), -+ void (*dtor)(void *, kmem_cache_t *, unsigned long), -+ int (*compare)(void *data, void *hashkey), -+ __u32 (*hash_function)(void *hashkey)) -+{ -+ int i; -+ struct hashlist *hl; -+ -+ if (!compare || !hash_function) -+ goto hlfailed; -+ -+ hl = kmalloc(sizeof(struct hashlist), GFP_ATOMIC); -+ if (!hl) goto hlfailed; -+ -+ hl->kmem = kmem_cache_create(name, size, 0, 0, ctor, dtor); -+ if (!hl->kmem) goto poolfailed; -+ -+ hl->hashtable = kmalloc( -+ sizeof(struct list_head) * bucketnum, GFP_ATOMIC); -+ if (!hl->hashtable) goto hashfailed; -+ -+ for (i = 0; i < bucketnum; i++) -+ INIT_LIST_HEAD(&hl->hashtable[i]); -+ -+ INIT_LIST_HEAD(&hl->sortedlist); -+ -+ hl->maxcount = max_entries; -+ hl->count = 0; -+ hl->bucketnum = bucketnum; -+ hl->compare = compare; -+ hl->hash_function = hash_function; -+ -+ return hl; -+ -+hashfailed: -+ kmem_cache_destroy(hl->kmem); -+ hl->kmem = NULL; -+ -+poolfailed: -+ kfree(hl); -+ -+hlfailed: -+ DEBUG(DBG_ERROR, "could not create hashlist"); -+ -+ return NULL; -+} -+ -+/** -+ * hashlist_destroy - Destroy hashlist -+ * @hashlist: hashlist to destroy -+ * -+ * Frees all memory allocated for a hashlist. -+ **/ -+void hashlist_destroy(struct hashlist *hashlist) -+{ -+ DEBUG_FUNC(); -+ -+ if (hashlist == NULL) return; -+ -+ if (hashlist->hashtable) { -+ kfree(hashlist->hashtable); -+ hashlist->hashtable = NULL; -+ } -+ -+ if (hashlist->kmem) { -+ kmem_cache_destroy(hashlist->kmem); -+ hashlist->kmem = NULL; -+ } -+ -+ kfree(hashlist); -+ -+ return; -+} -+ -+/* -+ * Insert a chain of entries to hashlist into correct order. The -+ * entries are assumed to have valid hashkeys. We use time_after_eq -+ * for comparing, since it handles wrap-around correctly, and the -+ * sortkey is usually jiffies. -+ */ -+static void sorted_insert(struct list_head *lh, struct hashlist_entry *he) -+{ -+ struct list_head *p; -+ struct hashlist_entry *hlp = NULL; -+ unsigned long sortkey = he->sortkey; -+ -+ if (list_empty(lh)) { -+ list_add(&he->sorted, lh); -+ return; -+ } -+ -+ list_for_each(p, lh) { -+ hlp = list_entry(p, typeof(*hlp), sorted); -+ if (time_after_eq(hlp->sortkey, sortkey)) { -+ list_add(&he->sorted, hlp->sorted.prev); -+ return; -+ } -+ } -+ list_add(&he->sorted, &hlp->sorted); -+} -+ -+/** -+ * hashlist_iterate - Apply function for all elements in a hash list -+ * @hashlist: pointer to hashlist -+ * @args: data to pass to the function -+ * @func: pointer to a function -+ * -+ * Apply arbitrary function @func to all elements in a hash list. -+ * @func must be a pointer to a function with the following prototype: -+ * int func(void *entry, void *arg, struct in6_addr *hashkey, unsigned -+ * long *sortkey). Function must return %ITERATOR_STOP, -+ * %ITERATOR_CONT or %ITERATOR_DELETE_ENTRY. %ITERATOR_STOP stops -+ * iterator and returns last return value from the function. -+ * %ITERATOR_CONT continues with iteration. %ITERATOR_DELETE_ENTRY -+ * deletes current entry from the hashlist. If function changes -+ * hashlist element's sortkey, iterator automatically schedules -+ * element to be reinserted after all elements have been processed. -+ */ -+int hashlist_iterate( -+ struct hashlist *hashlist, void *args, -+ hashlist_iterator_t func) -+{ -+ int res = ITERATOR_CONT; -+ unsigned long skey; -+ struct list_head *p, *n, repos; -+ struct hashlist_entry *he; -+ -+ DEBUG_FUNC(); -+ INIT_LIST_HEAD(&repos); -+ -+ list_for_each_safe(p, n, &hashlist->sortedlist) { -+ he = list_entry(p, typeof(*he), sorted); -+ if (res == ITERATOR_STOP) -+ break; -+ skey = he->sortkey; -+ res = func(he, args, &he->sortkey); -+ if (res == ITERATOR_DELETE_ENTRY) { -+ hashlist_delete(hashlist, he); -+ hashlist_free(hashlist, he); -+ } else if (skey != he->sortkey) { -+ /* iterator changed the sortkey, schedule for -+ * repositioning */ -+ list_move(&he->sorted, &repos); -+ } -+ } -+ list_for_each_safe(p, n, &repos) { -+ he = list_entry(p, typeof(*he), sorted); -+ sorted_insert(&hashlist->sortedlist, he); -+ } -+ return res; -+} -+ -+/** -+ * hashlist_alloc - Allocate memory for a hashlist entry -+ * @hashlist: hashlist for allocated entry -+ * @size: size of entry in bytes -+ * -+ * Allocates @size bytes memory from @hashlist->kmem. -+ **/ -+void *hashlist_alloc(struct hashlist *hashlist, int type) -+{ -+ if (hashlist == NULL) return NULL; -+ return kmem_cache_alloc(hashlist->kmem, type); -+} -+ -+/** -+ * hashlist_free - Free hashlist entry -+ * @hashlist: hashlist where @he is -+ * @he: entry to free -+ * -+ * Frees an allocated hashlist entry. -+ **/ -+void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he) -+{ -+ kmem_cache_free(hashlist->kmem, he); -+} -+ -+/** -+ * hashlist_add - Add element to hashlist -+ * @hashlist: pointer to hashlist -+ * @hashkey: hashkey for the element -+ * @sortkey: key for sorting -+ * @data: element data -+ * -+ * Add element to hashlist. Hashlist is also sorted in a linked list -+ * by @sortkey. -+ */ -+int hashlist_add(struct hashlist *hashlist, void *hashkey, -+ unsigned long sortkey, void *entry) -+{ -+ struct hashlist_entry *he = (struct hashlist_entry *)entry; -+ unsigned int hash; -+ -+ if (hashlist->count >= hashlist->maxcount) -+ return -1; -+ -+ hashlist->count++; -+ -+ /* link the entry to sorted order */ -+ he->sortkey = sortkey; -+ sorted_insert(&hashlist->sortedlist, he); -+ -+ /* hash the entry */ -+ hash = hashlist->hash_function(hashkey) % hashlist->bucketnum; -+ list_add(&he->hashlist, &hashlist->hashtable[hash]); -+ -+ return 0; -+} -+ -+/** -+ * hashlist_get_ex - Get element from hashlist -+ * @hashlist: hashlist -+ * @hashkey: hashkey of the desired entry -+ * -+ * Lookup entry with @hashkey from the hash table using @compare -+ * function for entry comparison. Returns entry on success, otherwise -+ * %NULL. -+ **/ -+struct hashlist_entry *hashlist_get_ex( -+ struct hashlist *hashlist, void *hashkey, -+ int (*compare)(void *data, void *hashkey)) -+{ -+ struct list_head *p, *bkt; -+ __u32 hash; -+ -+ hash = hashlist->hash_function(hashkey) % hashlist->bucketnum; -+ bkt = &hashlist->hashtable[hash]; -+ -+ /* scan the entries within the same hashbucket */ -+ list_for_each(p, bkt) { -+ struct hashlist_entry *he = list_entry(p, typeof(*he), -+ hashlist); -+ if (compare(he, hashkey) == 0) -+ return he; -+ } -+ -+ return NULL; -+} -+ -+/** -+ * hashlist_get - Get element from hashlist -+ * @hashlist: hashlist -+ * @hashkey: hashkey of the desired entry -+ * -+ * Lookup entry with @hashkey from the hash table. Returns entry on -+ * success, otherwise %NULL. -+ **/ -+struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey) -+{ -+ return hashlist_get_ex(hashlist, hashkey, hashlist->compare); -+} -+ -+/** -+ * hashlist_reposition - set entry to new position in the list -+ * @hashlist: hashlist -+ * @he: entry to reposition -+ * @sortkey: new sortkey of the entry -+ * -+ * If secondary order sortkey changes, entry must be repositioned in -+ * the sorted list. -+ **/ -+int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he, -+ unsigned long sortkey) -+{ -+ list_del(&he->sorted); -+ he->sortkey = sortkey; -+ sorted_insert(&hashlist->sortedlist, he); -+ -+ return 0; -+} -+ -+/** -+ * hashlist_delete - Delete entry from hashlist -+ * @hashlist: hashlist where entry is -+ * @he: entry to delete -+ * -+ * Deletes an entry from the hashlist and sorted list. -+ **/ -+void hashlist_delete(struct hashlist *hashlist, -+ struct hashlist_entry *he) -+{ -+ list_del_init(&he->hashlist); -+ list_del_init(&he->sorted); -+ -+ hashlist->count--; -+} -+ -+/** -+ * hashlist_get_first - Get first item from sorted list -+ * @hashlist: pointer to hashlist -+ * -+ * Returns first item in the secondary sort order. -+ **/ -+void * hashlist_get_first(struct hashlist *hashlist) -+{ -+ if (list_empty(&hashlist->sortedlist)) -+ return NULL; -+ -+ return list_entry(hashlist->sortedlist.next, struct hashlist_entry, sorted); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/hashlist.h -@@ -0,0 +1,63 @@ -+/* -+ * MIPL Mobile IPv6 Hashlist header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HASHLIST_H -+#define _HASHLIST_H -+ -+#define ITERATOR_ERR -1 -+#define ITERATOR_CONT 0 -+#define ITERATOR_STOP 1 -+#define ITERATOR_DELETE_ENTRY 2 -+ -+struct kmem_cache_t; -+ -+struct hashlist_entry { -+ unsigned long sortkey; -+ struct list_head sorted; -+ struct list_head hashlist; -+}; -+ -+struct hashlist * hashlist_create( -+ int bucketnum, int max_entries, size_t size, char *name, -+ void (*ctor)(void *, kmem_cache_t *, unsigned long), -+ void (*dtor)(void *, kmem_cache_t *, unsigned long), -+ int (*compare)(void *data, void *hashkey), -+ __u32 (*hash_function)(void *hashkey)); -+ -+void hashlist_destroy(struct hashlist *hashlist); -+ -+void *hashlist_alloc(struct hashlist *hashlist, int type); -+ -+void hashlist_free(struct hashlist *hashlist, struct hashlist_entry *he); -+ -+struct hashlist_entry *hashlist_get(struct hashlist *hashlist, void *hashkey); -+ -+struct hashlist_entry *hashlist_get_ex( -+ struct hashlist *hashlist, void *hashkey, -+ int (*compare)(void *data, void *hashkey)); -+ -+int hashlist_add(struct hashlist *hashlist, void *hashkey, -+ unsigned long sortkey, void *data); -+ -+void hashlist_delete(struct hashlist *hashlist, struct hashlist_entry *he); -+ -+/* iterator function */ -+typedef int (*hashlist_iterator_t)(void *, void *, unsigned long *); -+ -+int hashlist_iterate(struct hashlist *hashlist, void *args, -+ hashlist_iterator_t func); -+ -+void * hashlist_get_first(struct hashlist *hashlist); -+ -+int hashlist_reposition(struct hashlist *hashlist, struct hashlist_entry *he, -+ unsigned long sortkey); -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/hmac.c -@@ -0,0 +1,658 @@ -+/* Authentication algorithms -+ * -+ * Authors: -+ * Alexis Olivereau <Alexis.Olivereau@crm.mot.com> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Changes: -+ * Henrik Petander : Cleaned up unused parts -+ * -+ */ -+ -+#include <linux/sched.h> -+#include <linux/tty.h> -+#include <linux/types.h> -+#include <linux/slab.h> -+#include <linux/in6.h> -+ -+#include "hmac.h" -+#define LROLL(x, s) (((x) << (s)) | ((x) >> (32 - (s)))) -+ -+/* MD5 */ -+#define F(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -+#define G(x, y, z) ((y) ^ ((z) & ((x) ^ (y)))) -+#define H(x, y, z) ((x) ^ (y) ^ (z)) -+#define I(x, y, z) ((y) ^ ((x) | ~(z))) -+ -+#define FF(a, b, c, d, m, s, t) { \ -+ (a) += F ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+#define GG(a, b, c, d, m, s, t) { \ -+ (a) += G ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+#define HH(a, b, c, d, m, s, t) { \ -+ (a) += H ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+#define II(a, b, c, d, m, s, t) { \ -+ (a) += I ((b), (c), (d)) + (m) + (t); \ -+ (a) = LROLL((a), (s)); \ -+ (a) += (b); \ -+ } -+ -+#define s11 7 -+#define s12 12 -+#define s13 17 -+#define s14 22 -+#define s21 5 -+#define s22 9 -+#define s23 14 -+#define s24 20 -+#define s31 4 -+#define s32 11 -+#define s33 16 -+#define s34 23 -+#define s41 6 -+#define s42 10 -+#define s43 15 -+#define s44 21 -+ -+/* SHA-1 */ -+#define f(x, y, z) ((z) ^ ((x) & ((y) ^ (z)))) -+#define g(x, y, z) (((x) & (y)) | ((x) & (z)) | ((y) & (z))) -+#define h(x, y, z) ((x) ^ (y) ^ (z)) -+ -+#define K1 0x5a827999 -+#define K2 0x6ed9eba1 -+#define K3 0x8f1bbcdc -+#define K4 0xca62c1d6 -+ -+int ah_hmac_md5_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len) -+{ -+ int i; -+ int key_up4; -+ uint32_t ipad = 0x36363636; -+ uint8_t extkey[64]; -+ -+ ahp->key_auth = key; -+ ahp->key_auth_len = key_len; -+ ahp->context = (void *) kmalloc(sizeof(MD5_CTX), GFP_ATOMIC); -+ if (ahp->context == NULL) -+ return -1; -+ md5_init((MD5_CTX *) ahp->context); -+ if ((64 * sizeof(uint8_t)) < ahp->key_auth_len) { -+ printk("buffer overflow!"); -+ return -1; -+ } -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = ipad; -+ -+ md5_compute((MD5_CTX *) ahp->context, extkey, 64); -+ return 0; -+} -+ -+void ah_hmac_md5_loop(struct ah_processing *ahp, void *str, uint32_t len) -+{ -+ md5_compute((MD5_CTX *) ahp->context, str, len); -+} -+ -+void ah_hmac_md5_result(struct ah_processing *ahp, char *digest) -+{ -+ uint8_t inner[HMAC_MD5_HASH_LEN]; -+ int i; -+ int key_up4; -+ uint32_t opad = 0x5c5c5c5c; -+ uint8_t extkey[64]; -+ -+ md5_final((MD5_CTX *) ahp->context, inner); -+ md5_init((MD5_CTX *) ahp->context); -+ -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = opad; -+ -+ md5_compute((MD5_CTX *) ahp->context, extkey, 64); -+ md5_compute((MD5_CTX *) ahp->context, inner, HMAC_MD5_HASH_LEN); -+ -+ md5_final((MD5_CTX *) ahp->context, digest); -+ -+ kfree(ahp->context); -+} -+ -+int ah_hmac_sha1_init(struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len) -+{ -+ int i; -+ int key_up4; -+ uint32_t ipad = 0x36363636; -+ uint8_t extkey[64]; -+ -+ ahp->key_auth = key; -+ ahp->key_auth_len = key_len; -+ -+ ahp->context = (void *) kmalloc(sizeof(SHA1_CTX), GFP_ATOMIC); -+ //if (ahp->context == NULL) -+ // return -1; -+ -+ sha1_init((SHA1_CTX *) ahp->context); -+ -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ ipad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = ipad; -+ -+ sha1_compute((SHA1_CTX *) ahp->context, extkey, 64); -+ return 0; -+} -+ -+void ah_hmac_sha1_loop(struct ah_processing *ahp, void *str, uint32_t len) -+{ -+ if (!ahp) -+ return; -+ sha1_compute((SHA1_CTX *) ahp->context, str, len); -+} -+ -+void ah_hmac_sha1_result(struct ah_processing *ahp, char *digest) -+{ -+ uint8_t inner[HMAC_SHA1_HASH_LEN]; -+ int i; -+ int key_up4; -+ uint32_t opad = 0x5c5c5c5c; -+ uint8_t extkey[64]; -+ -+ if (!ahp) -+ return; -+ sha1_final((SHA1_CTX *) ahp->context, inner); -+ sha1_init((SHA1_CTX *) ahp->context); -+ -+ memcpy(extkey, ahp->key_auth, ahp->key_auth_len); -+ if (ahp->key_auth_len % 4) { -+ memset(extkey + ahp->key_auth_len, 0, -+ 4 - (ahp->key_auth_len % 4)); -+ } -+ key_up4 = ((ahp->key_auth_len + 0x3) & 0xFFFFFFFC) / 4; -+ -+ for (i = 0; i < key_up4; i++) -+ ((uint32_t *) extkey)[i] = ((uint32_t *) extkey)[i] ^ opad; -+ for (i = key_up4; i < 16; i++) -+ ((uint32_t *) extkey)[i] = opad; -+ -+ sha1_compute((SHA1_CTX *) ahp->context, extkey, 64); -+ sha1_compute((SHA1_CTX *) ahp->context, inner, -+ HMAC_SHA1_HASH_LEN); -+ -+ sha1_final((SHA1_CTX *) ahp->context, digest); -+ -+ kfree(ahp->context); -+} -+ -+void md5_init(MD5_CTX * ctx) -+{ -+ ctx->A = 0x67452301; -+ ctx->B = 0xefcdab89; -+ ctx->C = 0x98badcfe; -+ ctx->D = 0x10325476; -+ ctx->buf_cur = ctx->buf; -+ ctx->bitlen[0] = ctx->bitlen[1] = 0; -+ memset(ctx->buf, 0, 64); -+} -+ -+void md5_over_block(MD5_CTX * ctx, uint8_t * data) -+{ -+ uint32_t M[16]; -+ uint32_t a = ctx->A; -+ uint32_t b = ctx->B; -+ uint32_t c = ctx->C; -+ uint32_t d = ctx->D; -+ -+ create_M_blocks(M, data); -+ -+ /* Round 1 */ -+ FF(a, b, c, d, M[0], s11, 0xd76aa478); /* 1 */ -+ FF(d, a, b, c, M[1], s12, 0xe8c7b756); /* 2 */ -+ FF(c, d, a, b, M[2], s13, 0x242070db); /* 3 */ -+ FF(b, c, d, a, M[3], s14, 0xc1bdceee); /* 4 */ -+ FF(a, b, c, d, M[4], s11, 0xf57c0faf); /* 5 */ -+ FF(d, a, b, c, M[5], s12, 0x4787c62a); /* 6 */ -+ FF(c, d, a, b, M[6], s13, 0xa8304613); /* 7 */ -+ FF(b, c, d, a, M[7], s14, 0xfd469501); /* 8 */ -+ FF(a, b, c, d, M[8], s11, 0x698098d8); /* 9 */ -+ FF(d, a, b, c, M[9], s12, 0x8b44f7af); /* 10 */ -+ FF(c, d, a, b, M[10], s13, 0xffff5bb1); /* 11 */ -+ FF(b, c, d, a, M[11], s14, 0x895cd7be); /* 12 */ -+ FF(a, b, c, d, M[12], s11, 0x6b901122); /* 13 */ -+ FF(d, a, b, c, M[13], s12, 0xfd987193); /* 14 */ -+ FF(c, d, a, b, M[14], s13, 0xa679438e); /* 15 */ -+ FF(b, c, d, a, M[15], s14, 0x49b40821); /* 16 */ -+ -+ /* Round 2 */ -+ GG(a, b, c, d, M[1], s21, 0xf61e2562); /* 17 */ -+ GG(d, a, b, c, M[6], s22, 0xc040b340); /* 18 */ -+ GG(c, d, a, b, M[11], s23, 0x265e5a51); /* 19 */ -+ GG(b, c, d, a, M[0], s24, 0xe9b6c7aa); /* 20 */ -+ GG(a, b, c, d, M[5], s21, 0xd62f105d); /* 21 */ -+ GG(d, a, b, c, M[10], s22, 0x02441453); /* 22 */ -+ GG(c, d, a, b, M[15], s23, 0xd8a1e681); /* 23 */ -+ GG(b, c, d, a, M[4], s24, 0xe7d3fbc8); /* 24 */ -+ GG(a, b, c, d, M[9], s21, 0x21e1cde6); /* 25 */ -+ GG(d, a, b, c, M[14], s22, 0xc33707d6); /* 26 */ -+ GG(c, d, a, b, M[3], s23, 0xf4d50d87); /* 27 */ -+ GG(b, c, d, a, M[8], s24, 0x455a14ed); /* 28 */ -+ GG(a, b, c, d, M[13], s21, 0xa9e3e905); /* 29 */ -+ GG(d, a, b, c, M[2], s22, 0xfcefa3f8); /* 30 */ -+ GG(c, d, a, b, M[7], s23, 0x676f02d9); /* 31 */ -+ GG(b, c, d, a, M[12], s24, 0x8d2a4c8a); /* 32 */ -+ -+ /* Round 3 */ -+ HH(a, b, c, d, M[5], s31, 0xfffa3942); /* 33 */ -+ HH(d, a, b, c, M[8], s32, 0x8771f681); /* 34 */ -+ HH(c, d, a, b, M[11], s33, 0x6d9d6122); /* 35 */ -+ HH(b, c, d, a, M[14], s34, 0xfde5380c); /* 36 */ -+ HH(a, b, c, d, M[1], s31, 0xa4beea44); /* 37 */ -+ HH(d, a, b, c, M[4], s32, 0x4bdecfa9); /* 38 */ -+ HH(c, d, a, b, M[7], s33, 0xf6bb4b60); /* 39 */ -+ HH(b, c, d, a, M[10], s34, 0xbebfbc70); /* 40 */ -+ HH(a, b, c, d, M[13], s31, 0x289b7ec6); /* 41 */ -+ HH(d, a, b, c, M[0], s32, 0xeaa127fa); /* 42 */ -+ HH(c, d, a, b, M[3], s33, 0xd4ef3085); /* 43 */ -+ HH(b, c, d, a, M[6], s34, 0x4881d05); /* 44 */ -+ HH(a, b, c, d, M[9], s31, 0xd9d4d039); /* 45 */ -+ HH(d, a, b, c, M[12], s32, 0xe6db99e5); /* 46 */ -+ HH(c, d, a, b, M[15], s33, 0x1fa27cf8); /* 47 */ -+ HH(b, c, d, a, M[2], s34, 0xc4ac5665); /* 48 */ -+ -+ /* Round 4 */ -+ II(a, b, c, d, M[0], s41, 0xf4292244); /* 49 */ -+ II(d, a, b, c, M[7], s42, 0x432aff97); /* 50 */ -+ II(c, d, a, b, M[14], s43, 0xab9423a7); /* 51 */ -+ II(b, c, d, a, M[5], s44, 0xfc93a039); /* 52 */ -+ II(a, b, c, d, M[12], s41, 0x655b59c3); /* 53 */ -+ II(d, a, b, c, M[3], s42, 0x8f0ccc92); /* 54 */ -+ II(c, d, a, b, M[10], s43, 0xffeff47d); /* 55 */ -+ II(b, c, d, a, M[1], s44, 0x85845dd1); /* 56 */ -+ II(a, b, c, d, M[8], s41, 0x6fa87e4f); /* 57 */ -+ II(d, a, b, c, M[15], s42, 0xfe2ce6e0); /* 58 */ -+ II(c, d, a, b, M[6], s43, 0xa3014314); /* 59 */ -+ II(b, c, d, a, M[13], s44, 0x4e0811a1); /* 60 */ -+ II(a, b, c, d, M[4], s41, 0xf7537e82); /* 61 */ -+ II(d, a, b, c, M[11], s42, 0xbd3af235); /* 62 */ -+ II(c, d, a, b, M[2], s43, 0x2ad7d2bb); /* 63 */ -+ II(b, c, d, a, M[9], s44, 0xeb86d391); /* 64 */ -+ -+ ctx->A += a; -+ ctx->B += b; -+ ctx->C += c; -+ ctx->D += d; -+} -+ -+void create_M_blocks(uint32_t * M, uint8_t * data) -+{ -+#ifdef HAVE_LITTLE_ENDIAN -+ memcpy((uint8_t *) M, data, 64); -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+#ifdef HAVE_BIG_ENDIAN -+ int i; -+ for (i = 0; i < 16; i++, data += 4) { -+ ((uint8_t *) (&M[i]))[0] = data[3]; -+ ((uint8_t *) (&M[i]))[1] = data[2]; -+ ((uint8_t *) (&M[i]))[2] = data[1]; -+ ((uint8_t *) (&M[i]))[3] = data[0]; -+ } -+#endif /* HAVE_BIG_ENDIAN */ -+} -+ -+void md5_compute(MD5_CTX * ctx, uint8_t * data, uint32_t len) -+{ -+ uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f); -+ -+ /* First we update the bit length */ -+ if ((ctx->bitlen[0] += (len << 3)) < (len << 3)) -+ ctx->bitlen[1]++; -+ ctx->bitlen[1] += (len >> 29); /* len is expressed in bytes */ -+ -+ if (pos) { -+ /* Buffer is not empty */ -+ if (64 - pos >= len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ pos += len; -+ if (pos == 64) { -+ /* The current block is over */ -+ md5_over_block(ctx, ctx->buf); -+ ctx->buf_cur = ctx->buf; -+ } -+ return; -+ } else { -+ memcpy(ctx->buf_cur, data, 64 - pos); -+ md5_over_block(ctx, ctx->buf); -+ len -= (64 - pos); -+ data += (64 - pos); -+ ctx->buf_cur = ctx->buf; -+ } -+ } -+ while (len >= 64) { -+ md5_over_block(ctx, data); -+ len -= 64; -+ data += 64; -+ } -+ if (len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ } -+} -+ -+void md5_final(MD5_CTX * ctx, uint8_t * digest) -+{ -+ uint32_t rem_size; -+ uint8_t *buf_cur = ctx->buf_cur; -+ int i; -+ -+ rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f); -+ *(buf_cur++) = 0x80; -+ -+ if (rem_size > 8 + 1) { -+ /* We have enough room in the current block */ -+ for (i = 0; i < rem_size - 8 - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ } else { -+ /* We do not have enough room and need therefore to add a new -+ 64-byte block */ -+ for (i = 0; i < rem_size - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ md5_over_block(ctx, ctx->buf); -+ -+ buf_cur = ctx->buf; -+ for (i = 0; i < 64 - 8; i++) { -+ *(buf_cur++) = 0; -+ } -+ } -+#ifdef HAVE_LITTLE_ENDIAN -+ memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8); -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+#ifdef HAVE_BIG_ENDIAN -+ *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff; -+#endif /* HAVE_BIG_ENDIAN */ -+ -+ md5_over_block(ctx, ctx->buf); -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t)); -+ memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t)); -+ memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t)); -+ memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t)); -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+#ifdef HAVE_BIG_ENDIAN -+ digest[0] = ((ctx->A) >> 24) & 0xff; -+ digest[1] = ((ctx->A) >> 16) & 0xff; -+ digest[2] = ((ctx->A) >> 8) & 0xff; -+ digest[3] = ((ctx->A) >> 0) & 0xff; -+ digest[4] = ((ctx->B) >> 24) & 0xff; -+ digest[5] = ((ctx->B) >> 16) & 0xff; -+ digest[6] = ((ctx->B) >> 8) & 0xff; -+ digest[7] = ((ctx->B) >> 0) & 0xff; -+ digest[8] = ((ctx->C) >> 24) & 0xff; -+ digest[9] = ((ctx->C) >> 16) & 0xff; -+ digest[10] = ((ctx->C) >> 8) & 0xff; -+ digest[11] = ((ctx->C) >> 0) & 0xff; -+ digest[12] = ((ctx->D) >> 24) & 0xff; -+ digest[13] = ((ctx->D) >> 16) & 0xff; -+ digest[14] = ((ctx->D) >> 8) & 0xff; -+ digest[15] = ((ctx->D) >> 0) & 0xff; -+#endif /* HAVE_BIG_ENDIAN */ -+} -+ -+void sha1_init(SHA1_CTX * ctx) -+{ -+ ctx->A = 0x67452301; -+ ctx->B = 0xefcdab89; -+ ctx->C = 0x98badcfe; -+ ctx->D = 0x10325476; -+ ctx->E = 0xc3d2e1f0; -+ ctx->buf_cur = ctx->buf; -+ ctx->bitlen[0] = ctx->bitlen[1] = 0; -+ memset(ctx->buf, 0, 64); -+} -+ -+void sha1_over_block(SHA1_CTX * ctx, uint8_t * data) -+{ -+ int i; -+ uint32_t W[80]; -+ uint32_t a = ctx->A; -+ uint32_t b = ctx->B; -+ uint32_t c = ctx->C; -+ uint32_t d = ctx->D; -+ uint32_t e = ctx->E; -+ uint32_t temp; -+ -+ create_W_blocks(W, data); -+ -+ /* Round 1 */ -+ for (i = 0; i < 20; i++) { -+ temp = LROLL(a, 5) + f(b, c, d) + e + W[i] + K1; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ /* Round 2 */ -+ for (i = 20; i < 40; i++) { -+ temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K2; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ /* Round 3 */ -+ for (i = 40; i < 60; i++) { -+ temp = LROLL(a, 5) + g(b, c, d) + e + W[i] + K3; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ /* Round 4 */ -+ for (i = 60; i < 80; i++) { -+ temp = LROLL(a, 5) + h(b, c, d) + e + W[i] + K4; -+ e = d; -+ d = c; -+ c = LROLL(b, 30); -+ b = a; -+ a = temp; -+ } -+ -+ ctx->A += a; -+ ctx->B += b; -+ ctx->C += c; -+ ctx->D += d; -+ ctx->E += e; -+} -+ -+void create_W_blocks(uint32_t * W, uint8_t * data) -+{ -+ int i; -+ -+#ifdef HAVE_BIG_ENDIAN -+ memcpy((uint8_t *) W, data, 64); -+#endif /* HAVE_BIG_ENDIAN */ -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ for (i = 0; i < 16; i++, data += 4) { -+ ((uint8_t *) (&W[i]))[0] = data[3]; -+ ((uint8_t *) (&W[i]))[1] = data[2]; -+ ((uint8_t *) (&W[i]))[2] = data[1]; -+ ((uint8_t *) (&W[i]))[3] = data[0]; -+ } -+#endif /* HAVE_LITTLE_ENDIAN */ -+ for (i = 16; i < 80; i++) { -+ W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]; -+ W[i] = LROLL(W[i], 1); -+ } -+} -+ -+void sha1_compute(SHA1_CTX * ctx, uint8_t * data, uint32_t len) -+{ -+ uint8_t pos = ((ctx->bitlen[0] >> 3) & 0x3f); -+ -+ /* First we update the bit length */ -+ if ((ctx->bitlen[0] += (len << 3)) < (len << 3)) -+ ctx->bitlen[1]++; -+ ctx->bitlen[1] += (len >> 29); /* len is expressed in bytes */ -+ -+ if (pos) { -+ /* Buffer is not empty */ -+ if (64 - pos >= len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ pos += len; -+ if (pos == 64) { -+ /* The current block is over */ -+ sha1_over_block(ctx, ctx->buf); -+ ctx->buf_cur = ctx->buf; -+ } -+ return; -+ } else { -+ memcpy(ctx->buf_cur, data, 64 - pos); -+ sha1_over_block(ctx, ctx->buf); -+ len -= (64 - pos); -+ data += (64 - pos); -+ ctx->buf_cur = ctx->buf; -+ } -+ } -+ while (len >= 64) { -+ sha1_over_block(ctx, data); -+ len -= 64; -+ data += 64; -+ } -+ if (len) { -+ memcpy(ctx->buf_cur, data, len); -+ ctx->buf_cur += len; -+ } -+} -+ -+void sha1_final(SHA1_CTX * ctx, uint8_t * digest) -+{ -+ uint32_t rem_size; -+ uint8_t *buf_cur = ctx->buf_cur; -+ int i; -+ -+ rem_size = 64 - ((ctx->bitlen[0] >> 3) & 0x3f); -+ *(buf_cur++) = 0x80; -+ -+ if (rem_size > 8 + 1) { -+ /* We have enough room in the current block */ -+ for (i = 0; i < rem_size - 8 - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ } else { -+ /* We do not have enough room and need therefore to add a new -+ 64-byte block */ -+ for (i = 0; i < rem_size - 1; i++) { -+ *(buf_cur++) = 0; -+ } -+ sha1_over_block(ctx, ctx->buf); -+ -+ buf_cur = ctx->buf; -+ for (i = 0; i < 64 - 8; i++) { -+ *(buf_cur++) = 0; -+ } -+ } -+#ifdef HAVE_BIG_ENDIAN -+ memcpy(buf_cur, (uint8_t *) ctx->bitlen, 8); -+#endif /* HAVE_BIG_ENDIAN */ -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ *(buf_cur++) = (ctx->bitlen[1] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[1] >> 0) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 24) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 16) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 8) & 0xff; -+ *(buf_cur++) = (ctx->bitlen[0] >> 0) & 0xff; -+#endif /* HAVE_LITTLE_ENDIAN */ -+ -+ sha1_over_block(ctx, ctx->buf); -+ -+#ifdef HAVE_BIG_ENDIAN -+ memcpy(digest + 0, (uint8_t *) (&(ctx->A)), sizeof(uint32_t)); -+ memcpy(digest + 4, (uint8_t *) (&(ctx->B)), sizeof(uint32_t)); -+ memcpy(digest + 8, (uint8_t *) (&(ctx->C)), sizeof(uint32_t)); -+ memcpy(digest + 12, (uint8_t *) (&(ctx->D)), sizeof(uint32_t)); -+ memcpy(digest + 16, (uint8_t *) (&(ctx->E)), sizeof(uint32_t)); -+#endif /* HAVE_BIG_ENDIAN */ -+ -+#ifdef HAVE_LITTLE_ENDIAN -+ digest[0] = ((ctx->A) >> 24) & 0xff; -+ digest[1] = ((ctx->A) >> 16) & 0xff; -+ digest[2] = ((ctx->A) >> 8) & 0xff; -+ digest[3] = ((ctx->A) >> 0) & 0xff; -+ digest[4] = ((ctx->B) >> 24) & 0xff; -+ digest[5] = ((ctx->B) >> 16) & 0xff; -+ digest[6] = ((ctx->B) >> 8) & 0xff; -+ digest[7] = ((ctx->B) >> 0) & 0xff; -+ digest[8] = ((ctx->C) >> 24) & 0xff; -+ digest[9] = ((ctx->C) >> 16) & 0xff; -+ digest[10] = ((ctx->C) >> 8) & 0xff; -+ digest[11] = ((ctx->C) >> 0) & 0xff; -+ digest[12] = ((ctx->D) >> 24) & 0xff; -+ digest[13] = ((ctx->D) >> 16) & 0xff; -+ digest[14] = ((ctx->D) >> 8) & 0xff; -+ digest[15] = ((ctx->D) >> 0) & 0xff; -+ digest[16] = ((ctx->E) >> 24) & 0xff; -+ digest[17] = ((ctx->E) >> 16) & 0xff; -+ digest[18] = ((ctx->E) >> 8) & 0xff; -+ digest[19] = ((ctx->E) >> 0) & 0xff; -+#endif /* HAVE_LITTLE_ENDIAN */ -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/hmac.h -@@ -0,0 +1,94 @@ -+/* -+ * MIPL Mobile IPv6 Message authentication algorithms -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _HMAC_H -+#define _HMAC_H -+ -+#include <linux/types.h> -+#include <linux/in6.h> -+ -+#define HAVE_LITTLE_ENDIAN -+ -+#define NO_EXPIRY 1 /* For sec_as */ -+ -+#define ALG_AUTH_NONE 0 -+#define ALG_AUTH_HMAC_MD5 1 -+#define ALG_AUTH_HMAC_SHA1 2 -+ -+struct sec_as; -+struct ah_processing { -+ void *context; -+ struct sec_as *sas; -+ u_int8_t *key_auth; -+ u_int32_t key_auth_len; -+}; -+ -+struct antireplay { -+ u_int32_t count; -+ u_int32_t bitmap; -+}; -+ -+typedef struct { -+ u_int32_t A, B, C, D; -+ u_int32_t bitlen[2]; -+ u_int8_t* buf_cur; -+ u_int8_t buf[64]; -+} MD5_CTX; -+ -+typedef struct { -+ u_int32_t A, B, C, D, E; -+ u_int32_t bitlen[2]; -+ u_int8_t* buf_cur; -+ u_int8_t buf[64]; -+} SHA1_CTX; -+ -+ -+ -+int ah_hmac_md5_init (struct ah_processing *ahp, u_int8_t *key, u_int32_t key_len); -+void ah_hmac_md5_loop(struct ah_processing*, void*, u_int32_t); -+void ah_hmac_md5_result(struct ah_processing*, char*); -+int ah_hmac_sha1_init(struct ah_processing*, u_int8_t *key, u_int32_t key_len); -+void ah_hmac_sha1_loop(struct ah_processing*, void*, u_int32_t); -+void ah_hmac_sha1_result(struct ah_processing*, char*); -+ -+ -+#define AH_HDR_LEN 12 /* # of bytes for Next Header, Payload Length, -+ RESERVED, Security Parameters Index and -+ -+ Sequence Number Field */ -+ -+void md5_init(MD5_CTX *ctx); -+void md5_over_block(MD5_CTX *ctx, u_int8_t* data); -+void create_M_blocks(u_int32_t* M, u_int8_t* data); -+void md5_compute(MD5_CTX *ctx, u_int8_t* data, u_int32_t len); -+void md5_final(MD5_CTX *ctx, u_int8_t* digest); -+ -+void sha1_init(SHA1_CTX *ctx); -+void sha1_over_block(SHA1_CTX *ctx, u_int8_t* data); -+void create_W_blocks(u_int32_t* W, u_int8_t* data); -+void sha1_compute(SHA1_CTX *ctx, u_int8_t* data, u_int32_t len); -+void sha1_final(SHA1_CTX *ctx, u_int8_t* digest); -+ -+struct mipv6_acq { -+ struct in6_addr coa; -+ struct in6_addr haddr; -+ struct in6_addr peer; -+ u_int32_t spi; -+}; -+#define MIPV6_MAX_AUTH_DATA 20 -+ -+#define HMAC_MD5_HASH_LEN 16 -+#define HMAC_SHA1_HASH_LEN 20 -+#define HMAC_SHA1_KEY_SIZE 20 -+#define HMAC_MD5_ICV_LEN 12 /* RFC 2403 */ -+#define HMAC_SHA1_ICV_LEN 12 /* RFC 2404 */ -+ -+#endif /* _HMAC_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/ioctl_mn.c -@@ -0,0 +1,142 @@ -+/* -+ * Mobile Node IOCTL Control device -+ * -+ * Authors: -+ * Henrik Petander <lpetande@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/fs.h> -+#include <linux/poll.h> -+#include <linux/ioctl.h> -+#include <net/ipv6.h> -+#include <asm/uaccess.h> -+ -+#include "debug.h" -+#include "mdetect.h" -+#include "multiaccess_ctl.h" -+ -+/* Reserved for local / experimental use */ -+#define MAJOR_NUM 0xf9 -+ -+/* Get Care-of address information for Mobile Node */ -+#define IOCTL_GET_CAREOFADDR _IOWR(MAJOR_NUM, 9, void *) -+ -+#define MA_IOCTL_SET_IFACE_PREFERENCE _IOR (MAJOR_NUM, 13, void *) -+ -+/* The name of the device file */ -+#define CTLFILE "mipv6_dev" -+ -+static int inuse = 0; -+ -+static int mipv6_open(struct inode *inode, struct file *file) -+{ -+ DEBUG(DBG_INFO, "(%p)\n", file); -+ -+ if (inuse) -+ return -EBUSY; -+ -+ inuse++; -+ -+ MOD_INC_USE_COUNT; -+ -+ return 0; -+} -+ -+static int mipv6_close(struct inode *inode, struct file *file) -+{ -+ DEBUG(DBG_INFO, "(%p,%p)\n", inode, file); -+ inuse--; -+ -+ MOD_DEC_USE_COUNT; -+ -+ return 0; -+} -+ -+int mipv6_ioctl(struct inode *inode, struct file *file, -+ unsigned int ioctl_num, /* The number of the ioctl */ -+ unsigned long arg) /* The parameter to it */ -+{ -+ struct in6_addr careofaddr; -+ -+ /* Switch according to the ioctl called */ -+ switch (ioctl_num) { -+ case IOCTL_GET_CAREOFADDR: -+ DEBUG(DBG_DATADUMP, "IOCTL_GET_CAREOFADDR"); -+ /* First get home address from user and then look up -+ * the care-of address and return it -+ */ -+ if (copy_from_user(&careofaddr, (struct in6_addr *)arg, -+ sizeof(struct in6_addr)) < 0) { -+ DEBUG(DBG_WARNING, "Copy from user failed"); -+ return -EFAULT; -+ } -+ mipv6_get_care_of_address(&careofaddr, &careofaddr); -+ if (copy_to_user((struct in6_addr *)arg, &careofaddr, -+ sizeof(struct in6_addr)) < 0) { -+ DEBUG(DBG_WARNING, "copy_to_user failed"); -+ return -EFAULT; -+ } -+ break; -+ case MA_IOCTL_SET_IFACE_PREFERENCE: -+ DEBUG(DBG_INFO, "MA_IOCTL_SET_IFACE_PREFERENCE"); -+ ma_ctl_set_preference(arg); -+ break; -+ -+ default: -+ DEBUG(DBG_WARNING, "Unknown ioctl cmd (%d)", ioctl_num); -+ return -ENOENT; -+ } -+ return 0; -+} -+ -+struct file_operations Fops = { -+ owner: THIS_MODULE, -+ read: NULL, -+ write: NULL, -+ poll: NULL, -+ ioctl: mipv6_ioctl, -+ open: mipv6_open, -+ release: mipv6_close -+}; -+ -+ -+/* Initialize the module - Register the character device */ -+int mipv6_ioctl_mn_init(void) -+{ -+ int ret_val; -+ -+ /* Register the character device (atleast try) */ -+ ret_val = register_chrdev(MAJOR_NUM, CTLFILE, &Fops); -+ -+ /* Negative values signify an error */ -+ if (ret_val < 0) { -+ DEBUG(DBG_ERROR, "failed registering char device (err=%d)", -+ ret_val); -+ return ret_val; -+ } -+ -+ DEBUG(DBG_INFO, "Device number %x, success", MAJOR_NUM); -+ return 0; -+} -+ -+ -+/* Cleanup - unregister the appropriate file from /proc */ -+void mipv6_ioctl_mn_exit(void) -+{ -+ int ret; -+ /* Unregister the device */ -+ ret = unregister_chrdev(MAJOR_NUM, CTLFILE); -+ -+ /* If there's an error, report it */ -+ if (ret < 0) -+ DEBUG(DBG_ERROR, "errorcode: %d\n", ret); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mdetect.c -@@ -0,0 +1,1153 @@ -+/* -+ * Movement Detection Module -+ * -+ * Authors: -+ * Henrik Petander <lpetande@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Handles the L3 movement detection of mobile node and also -+ * changing of its routes. -+ * -+ */ -+ -+/* -+ * Changes: -+ * -+ * Nanno Langstraat : Locking fixes -+ * Venkata Jagana : Locking fix -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/errno.h> -+#include <linux/init.h> -+#include <linux/if_arp.h> -+#include <linux/route.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipglue.h> -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include "util.h" -+#include "mdetect.h" -+#include "mn.h" -+#include "debug.h" -+#include "multiaccess_ctl.h" -+ -+#define START 0 -+#define CONTINUE 1 -+#define OK 2 -+#define DEBUG_MDETECT 7 -+ -+#define DEF_RTR_POLL_IVAL 5 /* In seconds */ -+ -+#define NO_RTR 0 -+#define RTR_SUSPECT 1 -+#define CURR_RTR_OK 2 -+ -+#define RA_RCVD 0 -+#define NA_RCVD 1 -+#define TIMEOUT 2 -+ -+#define MIPV6_MDF_NONE 0x0 -+#define MIPV6_MDF_HAS_RTR_PREV 0x1 -+ -+#define ROUTER_REACHABLE 1 -+#define RADV_MISSED 2 -+#define NOT_REACHABLE 3 -+ -+/* R_TIME_OUT paramater is used to make the decision when to change the -+ * default router, if the current one is unreachable. 2s is pretty aggressive -+ * and may result in hopping between two routers. OTOH a small value enhances -+ * the performance -+ */ -+#define R_TIME_OUT 30*HZ -+ -+/* maximum RA interval for router unreachability detection */ -+#define MAX_RADV_INTERVAL 6*HZ /* 6000 ms... */ -+ -+/* Threshold for exponential resending of router solicitations */ -+#define RS_RESEND_LINEAR 10*HZ -+ -+#define EAGER_CELL_SWITCHING 1 -+#define LAZY_CELL_SWITCHING 0 -+#define RESPECT_DAD 1 -+ -+#define ROUTER_ADDRESS 0x20 -+ -+/* RA flags */ -+#define ND_RA_FLAG_MANAGED 0x80 -+#define ND_RA_FLAG_OTHER 0x40 -+#define ND_RA_FLAG_HA 0x20 -+ -+/* DAD flags for global and link local addresses */ -+ -+#define COA_TENTATIVE 0x10 -+#define LLADDR_TENTATIVE 0x01 -+ -+struct router { -+ struct list_head list; -+ struct in6_addr ll_addr; -+ struct in6_addr raddr; /* Also contains prefix */ -+ __u8 link_addr[MAX_ADDR_LEN]; /* link layer address */ -+ __u8 link_addr_len; -+ __u8 state; -+ __u8 is_current; -+ __u8 reachable; -+ int ifindex; -+ int pfix_len; /* Length of the network prefix */ -+ unsigned long lifetime; /* from ra */ -+ __u32 last_ns_sent; -+ __u32 last_ra_rcvd; -+ __u32 interval; /* ra interval in milliseconds, 0 if not set */ -+ int glob_addr; /*Whether raddr contains also routers global address*/ -+ __u8 flags; /* RA flags, for example ha */ -+ struct in6_addr CoA; /* care-off address used with this router */ -+ int extra_addr_route; -+}; -+ -+/* dad could also be RESPECT_DAD for duplicate address detection of -+ new care-of addresses */ -+static int dad = 0; -+ -+/* Only one choice, nothing else implemented */ -+int max_rtr_reach_time = DEF_RTR_POLL_IVAL; -+ -+ -+int eager_cell_switching = EAGER_CELL_SWITCHING; /* Can be set to 0 via proc */ -+static spinlock_t router_lock; -+static spinlock_t ho_lock; -+ -+static void coa_timer_handler(unsigned long arg); -+static void timer_handler(unsigned long foo); -+static struct router *curr_router = NULL, *next_router = NULL; -+static struct timer_list r_timer = { function: timer_handler }; -+static struct timer_list coa_timer = { function: coa_timer_handler }; -+#define MAX_ROUTERS 1000 -+static LIST_HEAD(rtr_list); -+static int num_routers = 0; -+static struct handoff *_ho = NULL; -+/* -+ * Functions for handling the default router list, which movement -+ * detection uses for avoiding loops etc. -+ */ -+ -+/* TODO: Send NS to router after MAX interval has passed from last RA */ -+static int mipv6_router_state(struct router *rtr) { -+ if (rtr->interval) { -+ if (time_before(jiffies, (rtr->last_ra_rcvd + (rtr->interval * HZ) / 1000))) -+ return ROUTER_REACHABLE; -+ else -+ return NOT_REACHABLE; -+ } -+ else -+ if (time_after(jiffies, rtr->last_ra_rcvd + (rtr->lifetime * HZ))) -+ return NOT_REACHABLE; -+ return ROUTER_REACHABLE; -+} -+ -+/* searches for a specific router or any router that is reachable, -+ * if address is NULL. Also deletes obsolete routers. -+ */ -+static void mipv6_router_gc(void) -+{ -+ struct router *curr = NULL; -+ struct list_head *lh, *lh_tmp; -+ -+ DEBUG_FUNC(); -+ -+ list_for_each_safe(lh, lh_tmp, &rtr_list) { -+ curr = list_entry(lh, struct router, list); -+ if (mipv6_router_state(curr) == NOT_REACHABLE && !curr->is_current) { -+ num_routers--; -+ list_del_init(&curr->list); -+ DEBUG(DBG_DATADUMP, "Deleting unreachable router %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&curr->raddr)); -+ kfree(curr); -+ } -+ else { -+ DEBUG(DBG_DATADUMP, "NOT Deleting router %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&curr->raddr)); -+ } -+ } -+} -+ -+static struct router *mipv6_rtr_get(struct in6_addr *search_addr) -+{ -+ struct router *rtr = NULL; -+ struct list_head *lh; -+ -+ DEBUG_FUNC(); -+ -+ if (search_addr == NULL) -+ return NULL; -+ list_for_each(lh, &rtr_list) { -+ rtr = list_entry(lh, struct router, list); -+ if(!ipv6_addr_cmp(search_addr, &rtr->raddr)) { -+ return rtr; -+ } -+ } -+ return NULL; -+} -+ -+/* -+ * Adds router to list -+ */ -+static struct router *mipv6_rtr_add(struct router *nrt) -+{ -+ -+ struct router *rptr; -+ -+ DEBUG_FUNC(); -+ -+ /* check if someone is trying DoS attack, or we just have some -+ memory leaks... */ -+ if (num_routers > MAX_ROUTERS) { -+ DEBUG(DBG_CRITICAL, -+ "failed to add new router, MAX_ROUTERS exceeded"); -+ return NULL; -+ } -+ -+ rptr = kmalloc(sizeof(struct router), GFP_ATOMIC); -+ if (rptr) { -+ memcpy(rptr, nrt, sizeof(struct router)); -+ list_add(&rptr->list, &rtr_list); -+ num_routers++; -+ } -+ DEBUG(DBG_INFO, "Adding router: %x:%x:%x:%x:%x:%x:%x:%x, " -+ "lifetime : %d sec, adv.interval: %d millisec", -+ NIPV6ADDR(&rptr->raddr), rptr->lifetime, rptr->interval); -+ -+ DEBUG(DBG_INFO, "num_routers after addition: %d", num_routers); -+ return rptr; -+} -+ -+/* Cleans up the list */ -+static void list_free(struct router **curr_router_p) -+{ -+ struct router *tmp; -+ struct list_head *lh, *lh_tmp; -+ -+ DEBUG_FUNC(); -+ -+ DEBUG(DBG_INFO, "Freeing the router list"); -+ /* set curr_router->prev_router and curr_router NULL */ -+ *curr_router_p = NULL; -+ list_for_each_safe(lh, lh_tmp, &rtr_list) { -+ tmp = list_entry(lh, struct router, list); -+ DEBUG(DBG_INFO, "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&tmp->ll_addr)); -+ list_del(&tmp->list); -+ kfree(tmp); -+ num_routers--; -+ } -+} -+ -+int rs_state = START; -+ -+/* Sends router solicitations to all valid devices -+ * source = link local address (of sending interface) -+ * dstaddr = all routers multicast address -+ * Solicitations are sent at an exponentially decreasing rate -+ * -+ * TODO: send solicitation first at a normal rate (from ipv6) and -+ * after that use the exponentially increasing intervals -+ */ -+static int rs_send(void) -+{ -+ struct net_device *dev; -+ struct in6_addr raddr, lladdr; -+ struct inet6_dev *in6_dev = NULL; -+ static int num_rs; -+ -+ if (rs_state == START) { -+ num_rs = 0; -+ rs_state = CONTINUE; -+ } else if (num_rs++ > MAX_RTR_SOLICITATIONS) -+ return HZ; -+ -+ ipv6_addr_all_routers(&raddr); -+ read_lock(&dev_base_lock); -+ -+ /* Send router solicitations to all interfaces */ -+ for (dev = dev_base; dev; dev = dev->next) { -+ if ((dev->flags & IFF_UP) && dev->type == ARPHRD_ETHER) { -+ DEBUG(DBG_DATADUMP, "Sending RS to device %s", -+ dev->name); -+ if (!ipv6_get_lladdr(dev, &lladdr)) { -+ ndisc_send_rs(dev, &lladdr, &raddr); -+ in6_dev = in6_dev_get(dev); -+ in6_dev->if_flags |= IF_RS_SENT; -+ in6_dev_put(in6_dev); -+ } else { -+ DEBUG(DBG_DATADUMP, "%s: device doesn't have link-local address!\n", dev->name); -+ continue; -+ } -+ } -+ -+ } -+ read_unlock(&dev_base_lock); -+ return RTR_SOLICITATION_INTERVAL; -+} -+ -+/* Create a new CoA for MN and also add a route to it if it is still tentative -+ to allow MN to get packets to the address immediately -+ */ -+static int form_coa(struct in6_addr *coa, struct in6_addr *pfix, -+ int plen, int ifindex) -+{ -+ struct net_device *dev; -+ struct inet6_dev *in6_dev; -+ int ret = 0; -+ -+ if ((dev = dev_get_by_index(ifindex)) == NULL) { -+ DEBUG(DBG_WARNING, "Device is not present"); -+ return -1; -+ } -+ if ((in6_dev = in6_dev_get(dev)) == NULL) { -+ DEBUG(DBG_WARNING, "inet6_dev is not present"); -+ dev_put(dev); -+ return -1; -+ } -+ coa->s6_addr32[0] = pfix->s6_addr32[0]; -+ coa->s6_addr32[1] = pfix->s6_addr32[1]; -+ -+ if (ipv6_generate_eui64(coa->s6_addr + 8, dev) && -+ ipv6_inherit_eui64(coa->s6_addr + 8, in6_dev)) { -+ in6_dev_put(in6_dev); -+ dev_put(dev); -+ return -1; -+ } -+ if (ipv6_chk_addr(coa, dev) == 0) { -+ DEBUG(DBG_WARNING, "care-of address still tentative"); -+ ret = 1; -+ } -+ DEBUG(DBG_INFO, "Formed new CoA: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(coa)); -+ -+ in6_dev_put(in6_dev); -+ dev_put(dev); -+ return ret; -+} -+ -+static inline int rtr_is_gw(struct router *rtr, struct rt6_info *rt) -+{ -+ return ((rt->rt6i_flags & RTF_GATEWAY) && -+ !ipv6_addr_cmp(&rt->rt6i_gateway, &rtr->ll_addr)); -+} -+ -+static inline int is_prefix_route(struct router *rtr, struct rt6_info *rt) -+{ -+ return (!(rt->rt6i_flags & RTF_GATEWAY) && -+ mipv6_prefix_compare(&rt->rt6i_dst.addr, &rtr->raddr, -+ rtr->pfix_len)); -+} -+ -+/* -+ * Function that determines whether given rt6_info should be destroyed -+ * (negative => destroy rt6_info, zero or positive => do nothing) -+ */ -+static int mn_route_cleaner(struct rt6_info *rt, void *arg) -+{ -+ int type; -+ -+ struct router *rtr = (struct router *)arg; -+ -+ int ret = -1; -+ -+ DEBUG_FUNC(); -+ -+ if (!rt || !rtr) { -+ DEBUG(DBG_ERROR, "mn_route_cleaner: rt or rtr NULL"); -+ return 0; -+ } -+ -+ /* Do not delete routes to local addresses or to multicast -+ * addresses, since we need them to get router advertisements -+ * etc. Multicast addresses are more tricky, but we don't -+ * delete them in any case. The routing mechanism is not optimal for -+ * multihoming. -+ * -+ * Also keep all new prefix routes, gateway routes through rtr and -+ * all remaining default routes (including those used for reverse -+ * tunneling) -+ */ -+ type = ipv6_addr_type(&rt->rt6i_dst.addr); -+ -+ if ((type & (IPV6_ADDR_MULTICAST | IPV6_ADDR_LINKLOCAL)) || -+ rt->rt6i_dev == &loopback_dev || rtr_is_gw(rtr, rt) || -+ is_prefix_route(rtr, rt) || (rt->rt6i_flags & RTF_DEFAULT)) -+ ret = 0; -+ -+ /* delete all others */ -+ -+ if (rt->rt6i_dev != &loopback_dev) { -+ DEBUG(DEBUG_MDETECT, -+ "%s route:\n" -+ "dev: %s,\n" -+ "gw: %x:%x:%x:%x:%x:%x:%x:%x,\n" -+ "flags: %x,\n" -+ "metric: %d,\n" -+ "src: %x:%x:%x:%x:%x:%x:%x:%x,\n" -+ "dst: %x:%x:%x:%x:%x:%x:%x:%x,\n" -+ "plen: %d\n", -+ (ret ? "Deleting" : "Keeping"), -+ rt->rt6i_dev->name, -+ NIPV6ADDR(&rt->rt6i_gateway), -+ rt->rt6i_flags, -+ rt->rt6i_metric, -+ NIPV6ADDR(&rt->rt6i_src.addr), -+ NIPV6ADDR(&rt->rt6i_dst.addr), -+ rt->rt6i_dst.plen); -+ } -+ return ret; -+} -+ -+/* -+ * Deletes old routes -+ */ -+static __inline__ void delete_routes(struct router *rtr) -+{ -+ DEBUG_FUNC(); -+ -+ /* Routing table is locked to ensure that nobody uses its */ -+ write_lock_bh(&rt6_lock); -+ DEBUG(DBG_INFO, "mipv6: Purging routes"); -+ /* TODO: Does not prune, should it? */ -+ fib6_clean_tree(&ip6_routing_table, -+ mn_route_cleaner, 0, rtr); -+ write_unlock_bh(&rt6_lock); -+ -+} -+ -+ -+static __inline__ void delete_coas(struct router *rtr) -+{ -+ struct net_device *dev; -+ struct inet6_dev *idev; -+ struct inet6_ifaddr *ifa; -+ -+ dev = dev_get_by_index(rtr->ifindex); -+ if (!dev) -+ return; -+ -+ idev = in6_dev_get(dev); -+ -+ if (idev) { -+ read_lock_bh(&idev->lock); -+ ifa = idev->addr_list; -+ while (ifa) { -+ int keep; -+ spin_lock(&ifa->lock); -+ -+ keep = (ifa->flags&(IFA_F_PERMANENT|IFA_F_HOMEADDR) || -+ !ipv6_addr_cmp(&ifa->addr, &rtr->CoA)); -+ -+ spin_unlock(&ifa->lock); -+ -+ if (keep) -+ ifa = ifa->if_next; -+ else { -+ in6_ifa_hold(ifa); -+ read_unlock_bh(&idev->lock); -+ -+ ipv6_del_addr(ifa); -+ -+ read_lock_bh(&idev->lock); -+ ifa = idev->addr_list; -+ } -+ } -+ read_unlock_bh(&idev->lock); -+ in6_dev_put(idev); -+ } -+ dev_put(dev); -+} -+ -+int next_mdet_state[3][3] = {{CURR_RTR_OK, NO_RTR, NO_RTR}, -+ {CURR_RTR_OK, CURR_RTR_OK, NO_RTR}, -+ {CURR_RTR_OK, CURR_RTR_OK, RTR_SUSPECT}}; -+ -+char *states[3] = {"NO_RTR", "RTR_SUSPECT", "CURR_RTR_OK"}; -+char *events[3] = {"RA_RCVD", "NA_RCVD", "TIMEOUT"}; -+ -+/* State transitions -+ * NO_RTR, RA_RCVD -> CURR_RTR_OK -+ * NO_RTR, NA_RCVD -> NO_RTR -+ * NO_RTR, TIMEOUT -> NO_RTR -+ -+ * RTR_SUSPECT, RA_RCVD -> CURR_RTR_OK -+ * RTR_SUSPECT, NA_RCVD -> CURR_RTR_OK -+ * RTR_SUSPECT, TIMEOUT -> NO_RTR -+ -+ * CURR_RTR_OK, RA_RCVD -> CURR_RTR_OK -+ * CURR_RTR_OK, NA_RCVD -> CURR_RTR_OK -+ * CURR_RTR_OK, TIMEOUT -> RTR_SUSPECT -+ */ -+static int _curr_state = NO_RTR; -+ -+#if 0 -+static int get_mdet_state(void){ -+ int state; -+ spin_lock_bh(&router_lock); -+ state = _curr_state; -+ spin_unlock_bh(&router_lock); -+ return state; -+} -+#endif -+ -+/* Needs to be called with router_lock locked */ -+static int mdet_statemachine(int event) -+{ -+ -+ if (event > 2 || _curr_state > 2) { -+ DEBUG(DBG_ERROR, "Got illegal event or curr_state"); -+ return -1; -+ } -+ -+ DEBUG(DBG_DATADUMP, "Got event %s and curr_state is %s", -+ events[event], states[_curr_state]); -+ -+ _curr_state = next_mdet_state[_curr_state][event]; -+ DEBUG(DBG_DATADUMP, "Next state is %s", states[_curr_state]); -+ return _curr_state; -+} -+ -+static void mipv6_do_ll_dad(int ifindex) -+{ -+ struct net_device *dev = dev_get_by_index(ifindex); -+ if (dev) { -+ struct in6_addr lladdr; -+ struct inet6_ifaddr *ifa; -+ if (!ipv6_get_lladdr(dev, &lladdr) && -+ (ifa = ipv6_get_ifaddr(&lladdr, dev)) != NULL) { -+ spin_lock_bh(&ifa->lock); -+ if (!(ifa->flags & IFA_F_TENTATIVE)) { -+ ifa->flags |= IFA_F_TENTATIVE; -+ spin_unlock_bh(&ifa->lock); -+ addrconf_dad_start(ifa, 0); -+ } else -+ spin_unlock_bh(&ifa->lock); -+ -+ } -+ dev_put(dev); -+ } -+} -+/* -+ * Changes the router, called from ndisc.c if mipv6_router_event -+ * returns true. -+ */ -+ -+static void mipv6_change_router(void) -+{ -+ struct in6_addr coa; -+ int ret, ifindex; -+ -+ DEBUG_FUNC(); -+ -+ -+ if (next_router == NULL) -+ return; -+ -+ spin_lock(&router_lock); -+ -+ -+ if (curr_router != NULL && -+ !ipv6_addr_cmp(&curr_router->ll_addr, &next_router->ll_addr)) { -+ DEBUG(DBG_INFO,"Trying to handoff from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&curr_router->ll_addr)); -+ DEBUG(DBG_INFO,"Trying to handoff to: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&next_router->ll_addr)); -+ next_router = NULL; /* Let's not leave dangling pointers */ -+ spin_unlock(&router_lock); -+ return; -+ } -+ ret = form_coa(&next_router->CoA, &next_router->raddr, -+ next_router->pfix_len, next_router->ifindex); -+ if (ret < 0) { -+ DEBUG(DBG_ERROR, "handoff: Creation of coa failed"); -+ spin_unlock(&router_lock); -+ return; -+ } else if (ret > 0) -+ next_router->flags |= COA_TENTATIVE; -+ -+ mdet_statemachine(RA_RCVD); /* TODO: What if DAD fails... */ -+ if (next_router->interval) -+ mod_timer(&r_timer, jiffies + -+ (next_router->interval * HZ)/1000); -+ else -+ mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ); -+ -+ -+ if (ret == 0) { -+ ipv6_addr_copy(&coa, &next_router->CoA); -+ ifindex = next_router->ifindex; -+ spin_unlock(&router_lock); -+ mipv6_mdet_finalize_ho(&coa, ifindex); -+ return; -+ } -+ spin_unlock(&router_lock); -+ -+} -+static unsigned long ns_send(void) -+{ -+ struct neighbour *neigh; -+ struct net_device *dev; -+ struct in6_addr *raddr; -+ -+ DEBUG(DBG_DATADUMP, "Sending Neighbour solicitation to default router to verify its reachability"); -+ if (!curr_router) -+ return HZ; -+ if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL) -+ return HZ; -+ if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) { -+ dev_put(dev); -+ return HZ; -+ } -+ if (curr_router->glob_addr) -+ raddr = &curr_router->raddr; -+ else -+ raddr = &curr_router->ll_addr; -+ -+ curr_router->last_ns_sent = jiffies; -+ ndisc_send_ns(dev, neigh, raddr, raddr, NULL); -+ -+ neigh_release(neigh); -+ dev_put(dev); -+ return HZ/5; /* Wait 200ms for a reply */ -+} -+ -+static int na_rcvd(void) -+{ -+ int neigh_ok = 0; -+ struct neighbour *neigh; -+ struct net_device *dev; -+ -+ if (!curr_router) -+ return 0; -+ if ((dev = dev_get_by_index(curr_router->ifindex)) == NULL) -+ return 0; -+ if ((neigh = ndisc_get_neigh(dev, &curr_router->ll_addr)) == NULL) { -+ dev_put(dev); -+ return 0; -+ } -+ if (neigh->flags & NTF_ROUTER && -+ (time_after(neigh->confirmed, curr_router->last_ns_sent) || -+ neigh->confirmed == curr_router->last_ns_sent)) { -+ neigh_ok = 1; -+ DEBUG(DBG_DATADUMP, "Mdetect event: NA rcvd from curr rtr"); -+ } else -+ DEBUG(DBG_DATADUMP, "Mdetect event: NA NOT rcvd from curr rtr within time limit"); -+ neigh_release(neigh); -+ dev_put(dev); -+ return neigh_ok; -+} -+ -+static void coa_timer_handler(unsigned long dummy) -+{ -+ -+ spin_lock_bh(&ho_lock); -+ if (_ho) { -+ DEBUG(DBG_INFO, "Starting handoff after DAD"); -+ mipv6_mobile_node_moved(_ho); -+ kfree(_ho); -+ _ho = NULL; -+ } -+ spin_unlock_bh(&ho_lock); -+} -+static void timer_handler(unsigned long foo) -+{ -+ unsigned long timeout; -+ int state; -+ spin_lock_bh(&router_lock); -+ -+ if (_curr_state != NO_RTR) -+ rs_state = START; -+ -+ if (_curr_state == RTR_SUSPECT && na_rcvd()) { -+ state = mdet_statemachine(NA_RCVD); -+ timeout = curr_router->interval ? curr_router->interval : max_rtr_reach_time * HZ; -+ } else { -+ state = mdet_statemachine(TIMEOUT); -+ if (state == NO_RTR) -+ timeout = rs_send(); -+ else /* RTR_SUSPECT */ -+ timeout = ns_send(); -+ } -+ if (!timeout) -+ timeout = HZ; -+ -+ mipv6_router_gc(); -+ mod_timer(&r_timer, jiffies + timeout); -+ spin_unlock_bh(&router_lock); -+} -+ -+/** -+ * mipv6_get_care_of_address - get node's care-of primary address -+ * @homeaddr: one of node's home addresses -+ * @coaddr: buffer to store care-of address -+ * -+ * Stores the current care-of address in the @coaddr, assumes -+ * addresses in EUI-64 format. Since node might have several home -+ * addresses caller MUST supply @homeaddr. If node is at home -+ * @homeaddr is stored in @coaddr. Returns 0 on success, otherwise a -+ * negative value. -+ **/ -+int mipv6_get_care_of_address( -+ struct in6_addr *homeaddr, struct in6_addr *coaddr) -+{ -+ -+ DEBUG_FUNC(); -+ -+ if (homeaddr == NULL) -+ return -1; -+ spin_lock_bh(&router_lock); -+ if (curr_router == NULL || mipv6_mn_is_at_home(homeaddr) || -+ mipv6_prefix_compare(homeaddr, &curr_router->raddr, 64) || -+ curr_router->flags&COA_TENTATIVE) { -+ DEBUG(DBG_INFO, -+ "mipv6_get_care_of_address: returning home address"); -+ ipv6_addr_copy(coaddr, homeaddr); -+ spin_unlock_bh(&router_lock); -+ return 0; -+ -+ } -+ -+ /* At home or address check failure probably due to dad wait */ -+ if (mipv6_prefix_compare(&curr_router->raddr, homeaddr, -+ curr_router->pfix_len) -+ || (dad == RESPECT_DAD && -+ (ipv6_chk_addr(coaddr, NULL) == 0))) { -+ ipv6_addr_copy(coaddr, homeaddr); -+ } else { -+ ipv6_addr_copy(coaddr, &curr_router->CoA); -+ } -+ -+ spin_unlock_bh(&router_lock); -+ return 0; -+} -+ -+int mipv6_mdet_del_if(int ifindex) -+{ -+ struct router *curr = NULL; -+ struct list_head *lh, *lh_tmp; -+ -+ spin_lock_bh(&router_lock); -+ list_for_each_safe(lh, lh_tmp, &rtr_list) { -+ curr = list_entry(lh, struct router, list); -+ if (curr->ifindex == ifindex) { -+ num_routers--; -+ list_del_init(&curr->list); -+ DEBUG(DBG_DATADUMP, "Deleting router %x:%x:%x:%x:%x:%x:%x:%x on interface %d", -+ NIPV6ADDR(&curr->raddr), ifindex); -+ if (curr_router == curr) -+ curr_router = NULL; -+ kfree(curr); -+ } -+ } -+ spin_unlock_bh(&router_lock); -+ return 0; -+} -+ -+void mipv6_mdet_retrigger_ho(void) -+{ -+ struct handoff ho; -+ -+ spin_lock_bh(&router_lock); -+ if (curr_router != NULL) { -+ ho.coa = &curr_router->CoA; -+ ho.plen = curr_router->pfix_len; -+ ho.ifindex = curr_router->ifindex; -+ ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr); -+ ho.home_address = (curr_router->glob_addr && -+ curr_router->flags&ND_RA_FLAG_HA); -+ } -+ spin_unlock_bh(&router_lock); -+ mipv6_mobile_node_moved(&ho); -+} -+ -+void mipv6_mdet_set_curr_rtr_reachable(int reachable) -+{ -+ spin_lock_bh(&router_lock); -+ if (curr_router != NULL) { -+ curr_router->reachable = reachable; -+ } -+ spin_unlock_bh(&router_lock); -+ -+} -+ -+int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex) -+{ -+ int dummy; -+ struct handoff ho; -+ struct router *tmp; -+ struct net_device *dev; -+ struct in6_addr ll_addr; -+ -+ spin_lock_bh(&router_lock); -+ -+ if (!next_router) { -+ spin_unlock_bh(&router_lock); -+ return 0; -+ } -+ -+ dev = dev_get_by_index(next_router->ifindex); -+ -+ if (ipv6_get_lladdr(dev, &ll_addr) == 0) { -+ if (ipv6_addr_cmp(&ll_addr, coa) == 0) -+ DEBUG(DBG_INFO, "DAD for link local address completed"); -+ next_router->flags &= ~LLADDR_TENTATIVE; -+ } -+ -+ dev_put(dev); -+ -+ if (mipv6_prefix_compare(coa, &next_router->CoA, -+ next_router->pfix_len)) { -+ DEBUG(DBG_INFO, "DAD for Care-of address completed"); -+ next_router->flags &= ~COA_TENTATIVE; -+ } -+ if (!(next_router->flags&LLADDR_TENTATIVE) && !(next_router->flags&COA_TENTATIVE)) { -+ DEBUG(DBG_INFO, "%s: Proceeding with handoff after DAD\n", __FUNCTION__); -+ tmp = curr_router; -+ curr_router = next_router; -+ curr_router->is_current = 1; -+ next_router = NULL; -+ curr_router->flags &= ~COA_TENTATIVE; -+ delete_routes(curr_router); -+ delete_coas(curr_router); -+ if (tmp) { -+ struct net_device *dev_old = dev_get_by_index(tmp->ifindex); -+ struct rt6_info *rt = NULL; -+ if (dev_old) { -+ rt = rt6_get_dflt_router(&tmp->ll_addr, dev_old); -+ dev_put(dev_old); -+ } -+ if (rt) -+ ip6_del_rt(rt, NULL); -+ tmp->is_current = 0; -+ } -+ -+ ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy); -+ ma_ctl_upd_iface(curr_router->ifindex, MA_IFACE_CURRENT, &dummy); -+ -+ -+ ho.coa = &curr_router->CoA; -+ ho.plen = curr_router->pfix_len; -+ ho.ifindex = curr_router->ifindex; -+ ipv6_addr_copy(&ho.rtr_addr, &curr_router->raddr); -+ ho.home_address = (curr_router->glob_addr && -+ curr_router->flags&ND_RA_FLAG_HA); -+ -+ spin_unlock_bh(&router_lock); -+ mipv6_mobile_node_moved(&ho); -+ } else -+ spin_unlock_bh(&router_lock); -+ return 0; -+} -+/* Decides whether router candidate is the same router as current rtr -+ * based on prefix / global addresses of the routers and their link local -+ * addresses -+ */ -+static int is_current_rtr(struct router *nrt, struct router *crt) -+{ -+ DEBUG_FUNC(); -+ -+ DEBUG(DEBUG_MDETECT, "Current router: " -+ "%x:%x:%x:%x:%x:%x:%x:%x and", NIPV6ADDR(&crt->raddr)); -+ DEBUG(DEBUG_MDETECT, "Candidate router: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&nrt->raddr)); -+ -+ return (!ipv6_addr_cmp(&nrt->raddr,&crt->raddr) && -+ !ipv6_addr_cmp(&nrt->ll_addr, &crt->ll_addr)); -+} -+ -+/* -+ * Change next router to nrtr -+ * Returns 1, if router has been changed. -+ */ -+ -+static int change_next_rtr(struct router *nrtr, struct router *ortr) -+{ -+ int changed = 0; -+ DEBUG_FUNC(); -+ -+ if (!next_router || ipv6_addr_cmp(&nrtr->raddr, &next_router->raddr)) { -+ changed = 1; -+ } -+ next_router = nrtr; -+ return changed; -+} -+static int clean_ncache(struct router *nrt, struct router *ort, int same_if) -+{ -+ struct net_device *ortdev; -+ DEBUG_FUNC(); -+ -+ /* Always call ifdown after a handoff to ensure proper routing */ -+ -+ if (!ort) -+ return 0; -+ if ((ortdev = dev_get_by_index(ort->ifindex)) == NULL) { -+ DEBUG(DBG_WARNING, "Device is not present"); -+ return -1; -+ } -+ neigh_ifdown(&nd_tbl, ortdev); -+ dev_put(ortdev); -+ return 0; -+} -+ -+static int mdet_get_if_preference(int ifi) -+{ -+ int pref = 0; -+ -+ DEBUG_FUNC(); -+ -+ pref = ma_ctl_get_preference(ifi); -+ -+ DEBUG(DEBUG_MDETECT, "ifi: %d preference %d", ifi, pref); -+ -+ return pref; -+} -+ -+/* -+ * Called from mipv6_mn_ra_rcv to determine whether to do a handoff. -+ */ -+static int mipv6_router_event(struct router *rptr) -+{ -+ struct router *nrt = NULL; -+ int new_router = 0, same_if = 1; -+ int oldstate = _curr_state; -+ int addrtype = ipv6_addr_type(&rptr->raddr); -+ -+ DEBUG_FUNC(); -+ -+ if (rptr->lifetime == 0) -+ return MIPV6_IGN_RTR; -+ DEBUG(DEBUG_MDETECT, "Received a RA from router: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", NIPV6ADDR(&rptr->raddr)); -+ spin_lock(&router_lock); -+ -+ /* Add or update router entry */ -+ if ((nrt = mipv6_rtr_get(&rptr->raddr)) == NULL) { -+ if (addrtype == IPV6_ADDR_ANY || (nrt = mipv6_rtr_add(rptr)) == NULL) { -+ spin_unlock(&router_lock); -+ return MIPV6_IGN_RTR; -+ } -+ DEBUG(DBG_INFO, "Router not on list,adding it to the list"); -+ new_router = 1; -+ } -+ nrt->last_ra_rcvd = jiffies; -+ nrt->state = ROUTER_REACHABLE; -+ nrt->interval = rptr->interval; -+ nrt->lifetime = rptr->lifetime; -+ nrt->ifindex = rptr->ifindex; -+ nrt->flags = rptr->flags; -+ nrt->glob_addr = rptr->glob_addr; -+ -+ /* Whether from current router */ -+ if (curr_router && curr_router->reachable && -+ is_current_rtr(nrt, curr_router)) { -+ if (nrt->interval) -+ mod_timer(&r_timer, jiffies + (nrt->interval * HZ)/1000); -+ else -+ mod_timer(&r_timer, jiffies + max_rtr_reach_time * HZ); -+ mdet_statemachine(RA_RCVD); -+ spin_unlock(&router_lock); -+ return MIPV6_ADD_RTR; -+ } else if (oldstate == NO_RTR) { -+ rt6_purge_dflt_routers(0); /* For multiple interface case */ -+ DEBUG(DBG_INFO, "No router or router not reachable, switching to new one"); -+ goto handoff; -+ } -+ if (!curr_router) { -+ /* Startup */ -+ goto handoff; -+ } -+ /* Router behind same interface as current one ?*/ -+ same_if = (nrt->ifindex == curr_router->ifindex); -+ /* Switch to new router behind same interface if eager cell -+ * switching is used or if the interface is preferred -+ */ -+ if ((new_router && eager_cell_switching && same_if) || -+ (mdet_get_if_preference(nrt->ifindex) > -+ mdet_get_if_preference(curr_router->ifindex))) { -+ DEBUG(DBG_INFO, "Switching to new router."); -+ goto handoff; -+ } -+ -+ /* No handoff, don't add default route */ -+ DEBUG(DEBUG_MDETECT, "Ignoring RA"); -+ spin_unlock(&router_lock); -+ return MIPV6_IGN_RTR; -+handoff: -+ clean_ncache(nrt, curr_router, same_if); -+ nrt->reachable = 1; -+ if (same_if && change_next_rtr(nrt, curr_router)) { -+ mipv6_do_ll_dad(nrt->ifindex); -+ nrt->flags |= LLADDR_TENTATIVE; -+ } -+ spin_unlock(&router_lock); -+ -+ return MIPV6_CHG_RTR; -+} -+ -+/* -+ * Called from ndisc.c's router_discovery. -+ */ -+ -+static inline int ret_to_ha(struct in6_addr *addr) -+{ -+ int res = 0; -+ struct mn_info *minfo; -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_ha(addr); -+ if (minfo != NULL) { -+ spin_lock(&minfo->lock); -+ if (minfo->has_home_reg) { -+ res = 1; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock(&mn_info_lock); -+ return res; -+} -+ -+static int mipv6_mn_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts) -+{ -+ int ifi = ((struct inet6_skb_parm *)skb->cb)->iif; -+ struct ra_msg *ra = (struct ra_msg *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct router nrt; -+ struct in6_addr *ha = NULL; -+ u8 *lladdr = NULL; -+ int res; -+ DEBUG_FUNC(); -+ -+ memset(&nrt, 0, sizeof(struct router)); -+ -+ if (ra->icmph.icmp6_home_agent) { -+ nrt.flags |= ND_RA_FLAG_HA; -+ DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_HA up"); -+ } -+ -+ if (ra->icmph.icmp6_addrconf_managed) { -+ nrt.flags |= ND_RA_FLAG_MANAGED; -+ DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_MANAGED up"); -+ } -+ -+ if (ra->icmph.icmp6_addrconf_other) { -+ nrt.flags |= ND_RA_FLAG_OTHER; -+ DEBUG(DBG_DATADUMP, "RA has ND_RA_FLAG_OTHER up"); -+ } -+ -+ ipv6_addr_copy(&nrt.ll_addr, saddr); -+ nrt.ifindex = ifi; -+ nrt.lifetime = ntohs(ra->icmph.icmp6_rt_lifetime); -+ -+ if (ndopts->nd_opts_src_lladdr) { -+ lladdr = (u8 *) ndopts->nd_opts_src_lladdr+2; -+ nrt.link_addr_len = skb->dev->addr_len; -+ memcpy(nrt.link_addr, lladdr, nrt.link_addr_len); -+ } -+ if (ndopts->nd_opts_pi) { -+ struct nd_opt_hdr *p; -+ for (p = ndopts->nd_opts_pi; -+ p; -+ p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) { -+ struct prefix_info *pinfo; -+ int update = 0; -+ -+ pinfo = (struct prefix_info *) p; -+ -+ if (!pinfo->autoconf) -+ continue; -+ -+ if ((pinfo->router_address && -+ (update = ret_to_ha(&pinfo->prefix))) || -+ ipv6_addr_type(&nrt.raddr) != IPV6_ADDR_UNICAST) { -+ ipv6_addr_copy(&nrt.raddr, &pinfo->prefix); -+ nrt.pfix_len = pinfo->prefix_len; -+ if (pinfo->router_address) -+ nrt.glob_addr = 1; -+ else -+ nrt.glob_addr = 0; -+ if (update) -+ ha = &pinfo->prefix; -+ DEBUG(DBG_DATADUMP, "Address of the received " -+ "prefix info option: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&nrt.raddr)); -+ DEBUG(DBG_DATADUMP, "the length of the prefix is %d", -+ nrt.pfix_len); -+ } -+ } -+ } -+ if (ndopts->nd_opts_rai) { -+ nrt.interval = ntohl(*(__u32 *)(ndopts->nd_opts_rai+4)); -+ DEBUG(DBG_DATADUMP, -+ "received router interval option with interval : %d ", -+ nrt.interval / HZ); -+ -+ if (nrt.interval > MAX_RADV_INTERVAL) { -+ nrt.interval = 0; -+ DEBUG(DBG_DATADUMP, "but we are using: %d, " -+ "because interval>MAX_RADV_INTERVAL", -+ nrt.interval / HZ); -+ } -+ } -+ -+ res = mipv6_router_event(&nrt); -+ -+ if (ha && lladdr) { -+ mipv6_mn_ha_nd_update(__dev_get_by_index(ifi), ha, lladdr); -+ } -+ return res; -+} -+ -+int __init mipv6_initialize_mdetect(void) -+{ -+ -+ DEBUG_FUNC(); -+ -+ spin_lock_init(&router_lock); -+ spin_lock_init(&ho_lock); -+ init_timer(&coa_timer); -+ init_timer(&r_timer); -+ r_timer.expires = jiffies + HZ; -+ add_timer(&r_timer); -+ -+ /* Actual HO, also deletes old routes after the addition of new ones -+ in ndisc */ -+ MIPV6_SETCALL(mipv6_change_router, mipv6_change_router); -+ -+ MIPV6_SETCALL(mipv6_ra_rcv, mipv6_mn_ra_rcv); -+ -+ return 0; -+} -+ -+int __exit mipv6_shutdown_mdetect() -+{ -+ -+ DEBUG_FUNC(); -+ -+ MIPV6_RESETCALL(mipv6_ra_rcv); -+ MIPV6_RESETCALL(mipv6_change_router); -+ spin_lock_bh(&router_lock); -+ spin_lock(&ho_lock); -+ del_timer(&coa_timer); -+ del_timer(&r_timer); -+ /* Free the memory allocated by router list */ -+ list_free(&curr_router); -+ if (_ho) -+ kfree(_ho); -+ spin_unlock(&ho_lock); -+ spin_unlock_bh(&router_lock); -+ return 0; -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mdetect.h -@@ -0,0 +1,37 @@ -+/* -+ * MIPL Mobile IPv6 Movement detection module header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MDETECT_H -+#define _MDETECT_H -+ -+struct handoff { -+ int home_address; /* Is the coa a home address */ -+ int ifindex; -+ int plen; -+ struct in6_addr *coa; -+ struct in6_addr rtr_addr; /* Prefix or rtr address if coa is home address */ -+}; -+ -+int mipv6_initialize_mdetect(void); -+ -+int mipv6_shutdown_mdetect(void); -+ -+int mipv6_get_care_of_address(struct in6_addr *homeaddr, struct in6_addr *coa); -+ -+int mipv6_mdet_del_if(int ifindex); -+ -+int mipv6_mdet_finalize_ho(const struct in6_addr *coa, const int ifindex); -+ -+void mipv6_mdet_retrigger_ho(void); -+ -+void mipv6_mdet_set_curr_rtr_reachable(int reachable); -+ -+#endif /* _MDETECT_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp.c -@@ -0,0 +1,342 @@ -+/** -+ * Generic icmp routines -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi>, -+ * Ville Nuorvala <vnuorval@tcs.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/icmpv6.h> -+#include <net/checksum.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+#include <net/mipglue.h> -+ -+#include "debug.h" -+#include "bcache.h" -+#include "mipv6_icmp.h" -+#include "config.h" -+ -+struct mipv6_icmpv6_msg { -+ struct icmp6hdr icmph; -+ __u8 *data; -+ struct in6_addr *daddr; -+ int len; -+ __u32 csum; -+}; -+ -+#define MIPV6_ICMP_HOP_LIMIT 64 -+ -+static struct socket *mipv6_icmpv6_socket = NULL; -+static __u16 identifier = 0; -+ -+int mipv6_icmpv6_no_rcv(struct sk_buff *skb) -+{ -+ return 0; -+} -+ -+static int mipv6_icmpv6_xmit_holder = -1; -+ -+static int mipv6_icmpv6_xmit_lock_bh(void) -+{ -+ if (!spin_trylock(&mipv6_icmpv6_socket->sk->lock.slock)) { -+ if (mipv6_icmpv6_xmit_holder == smp_processor_id()) -+ return -EAGAIN; -+ spin_lock(&mipv6_icmpv6_socket->sk->lock.slock); -+ } -+ mipv6_icmpv6_xmit_holder = smp_processor_id(); -+ return 0; -+} -+ -+static __inline__ int mipv6_icmpv6_xmit_lock(void) -+{ -+ int ret; -+ local_bh_disable(); -+ ret = mipv6_icmpv6_xmit_lock_bh(); -+ if (ret) -+ local_bh_enable(); -+ return ret; -+} -+ -+static void mipv6_icmpv6_xmit_unlock_bh(void) -+{ -+ mipv6_icmpv6_xmit_holder = -1; -+ spin_unlock(&mipv6_icmpv6_socket->sk->lock.slock); -+} -+ -+static __inline__ void mipv6_icmpv6_xmit_unlock(void) -+{ -+ mipv6_icmpv6_xmit_unlock_bh(); -+ local_bh_enable(); -+} -+ -+ -+/** -+ * mipv6_icmpv6_dest_unreach - Destination Unreachable ICMP error message handler -+ * @skb: buffer containing ICMP error message -+ * -+ * Special Mobile IPv6 ICMP handling. If Correspondent Node receives -+ * persistent ICMP Destination Unreachable messages for a destination -+ * in its Binding Cache, the binding should be deleted. See draft -+ * section 8.8. -+ **/ -+static int mipv6_icmpv6_rcv_dest_unreach(struct sk_buff *skb) -+{ -+ struct icmp6hdr *icmph = (struct icmp6hdr *) skb->h.raw; -+ struct ipv6hdr *ipv6h = (struct ipv6hdr *) (icmph + 1); -+ int left = (skb->tail - skb->h.raw) - sizeof(*icmph)- sizeof(ipv6h); -+ struct ipv6_opt_hdr *eh; -+ struct rt2_hdr *rt2h = NULL; -+ struct in6_addr *daddr = &ipv6h->daddr; -+ struct in6_addr *saddr = &ipv6h->saddr; -+ int hdrlen, nexthdr = ipv6h->nexthdr; -+ struct mipv6_bce bce; -+ DEBUG_FUNC(); -+ -+ eh = (struct ipv6_opt_hdr *) (ipv6h + 1); -+ -+ while (left > 0) { -+ if (nexthdr != NEXTHDR_HOP && nexthdr != NEXTHDR_DEST && -+ nexthdr != NEXTHDR_ROUTING) -+ return 0; -+ -+ hdrlen = ipv6_optlen(eh); -+ if (hdrlen > left) -+ return 0; -+ -+ if (nexthdr == NEXTHDR_ROUTING) { -+ struct ipv6_rt_hdr *rth = (struct ipv6_rt_hdr *) eh; -+ -+ if (rth->type == IPV6_SRCRT_TYPE_2) { -+ if (hdrlen != sizeof(struct rt2_hdr)) -+ return 0; -+ -+ rt2h = (struct rt2_hdr *) rth; -+ -+ if (rt2h->rt_hdr.segments_left > 0) -+ daddr = &rt2h->addr; -+ break; -+ } -+ } -+ /* check for home address option in case this node is a MN */ -+ if (nexthdr == NEXTHDR_DEST) { -+ __u8 *raw = (__u8 *) eh; -+ __u16 i = 2; -+ while (1) { -+ struct mipv6_dstopt_homeaddr *hao; -+ -+ if (i + sizeof (*hao) > hdrlen) -+ break; -+ -+ hao = (struct mipv6_dstopt_homeaddr *) &raw[i]; -+ -+ if (hao->type == MIPV6_TLV_HOMEADDR && -+ hao->length == sizeof(struct in6_addr)) { -+ saddr = &hao->addr; -+ break; -+ } -+ if (hao->type) -+ i += hao->length + 2; -+ else -+ i++; -+ } -+ -+ } -+ nexthdr = eh->nexthdr; -+ eh = (struct ipv6_opt_hdr *) ((u8 *) eh + hdrlen); -+ left -= hdrlen; -+ } -+ if (rt2h == NULL) return 0; -+ -+ if (mipv6_bcache_get(daddr, saddr, &bce) == 0 && !(bce.flags&HOME_REGISTRATION)) { -+ /* A primitive algorithm for detecting persistent ICMP destination unreachable messages */ -+ if (bce.destunr_count && -+ time_after(jiffies, -+ bce.last_destunr + MIPV6_DEST_UNR_IVAL*HZ)) -+ bce.destunr_count = 0; -+ -+ bce.destunr_count++; -+ -+ mipv6_bcache_icmp_err(daddr, saddr, bce.destunr_count); -+ -+ if (bce.destunr_count > MIPV6_MAX_DESTUNREACH && mipv6_bcache_delete(daddr, saddr, CACHE_ENTRY) == 0) { -+ DEBUG(DBG_INFO, "Deleted bcache entry " -+ "%x:%x:%x:%x:%x:%x:%x:%x " -+ "%x:%x:%x:%x:%x:%x:%x:%x (reason: " -+ "%d dest unreachables) ", -+ NIPV6ADDR(daddr), NIPV6ADDR(saddr), bce.destunr_count); -+ } -+ } -+ return 0; -+} -+ -+static int mipv6_icmpv6_getfrag(const void *data, struct in6_addr *saddr, -+ char *buff, unsigned int offset, -+ unsigned int len) -+{ -+ struct mipv6_icmpv6_msg *msg = (struct mipv6_icmpv6_msg *) data; -+ struct icmp6hdr *icmph; -+ __u32 csum; -+ -+ if (offset) { -+ msg->csum = csum_partial_copy_nocheck(msg->data + offset - -+ sizeof(*icmph), buff, -+ len, msg->csum); -+ return 0; -+ } -+ -+ csum = csum_partial_copy_nocheck((__u8 *) &msg->icmph, buff, -+ sizeof(*icmph), msg->csum); -+ -+ csum = csum_partial_copy_nocheck(msg->data, buff + sizeof(*icmph), -+ len - sizeof(*icmph), csum); -+ -+ icmph = (struct icmp6hdr *) buff; -+ -+ icmph->icmp6_cksum = csum_ipv6_magic(saddr, msg->daddr, msg->len, -+ IPPROTO_ICMPV6, csum); -+ return 0; -+} -+ -+/** -+ * mipv6_icmpv6_send - generic icmpv6 message send -+ * @daddr: destination address -+ * @saddr: source address -+ * @type: icmp type -+ * @code: icmp code -+ * @id: packet identifier. If null, uses internal counter to get new id -+ * @data: packet data -+ * @datalen: length of data in bytes -+ */ -+void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr, int type, -+ int code, __u16 *id, __u16 flags, void *data, int datalen) -+{ -+ struct sock *sk = mipv6_icmpv6_socket->sk; -+ struct flowi fl; -+ struct mipv6_icmpv6_msg msg; -+ -+ DEBUG_FUNC(); -+ -+ fl.proto = IPPROTO_ICMPV6; -+ fl.fl6_dst = daddr; -+ fl.fl6_src = saddr; -+ fl.fl6_flowlabel = 0; -+ fl.uli_u.icmpt.type = type; -+ fl.uli_u.icmpt.code = code; -+ -+ msg.icmph.icmp6_type = type; -+ msg.icmph.icmp6_code = code; -+ msg.icmph.icmp6_cksum = 0; -+ -+ if (id) -+ msg.icmph.icmp6_identifier = htons(*id); -+ else -+ msg.icmph.icmp6_identifier = htons(identifier++); -+ -+ msg.icmph.icmp6_sequence = htons(flags); -+ msg.data = data; -+ msg.csum = 0; -+ msg.len = datalen + sizeof(struct icmp6hdr); -+ msg.daddr = daddr; -+ -+ if (mipv6_icmpv6_xmit_lock()) -+ return; -+ -+ ip6_build_xmit(sk, mipv6_icmpv6_getfrag, &msg, &fl, msg.len, NULL, -1, -+ MSG_DONTWAIT); -+ -+ ICMP6_INC_STATS_BH(Icmp6OutMsgs); -+ mipv6_icmpv6_xmit_unlock(); -+} -+ -+/** -+ * icmp6_rcv - ICMPv6 receive and multiplex -+ * @skb: buffer containing ICMP message -+ * -+ * Generic ICMPv6 receive function to multiplex messages to approriate -+ * handlers. Only used for ICMP messages with special handling in -+ * Mobile IPv6. -+ **/ -+static void icmp6_rcv(struct sk_buff *skb) -+{ -+ struct icmp6hdr *hdr; -+ -+ if (skb_is_nonlinear(skb) && -+ skb_linearize(skb, GFP_ATOMIC) != 0) { -+ kfree_skb(skb); -+ return; -+ } -+ __skb_push(skb, skb->data-skb->h.raw); -+ -+ hdr = (struct icmp6hdr *) skb->h.raw; -+ -+ switch (hdr->icmp6_type) { -+ case ICMPV6_DEST_UNREACH: -+ mipv6_icmpv6_rcv_dest_unreach(skb); -+ break; -+ -+ case ICMPV6_PARAMPROB: -+ mip6_fn.icmpv6_paramprob_rcv(skb); -+ break; -+ -+ case MIPV6_DHAAD_REPLY: -+ mip6_fn.icmpv6_dhaad_rep_rcv(skb); -+ break; -+ -+ case MIPV6_PREFIX_ADV: -+ mip6_fn.icmpv6_pfxadv_rcv(skb); -+ break; -+ -+ case MIPV6_DHAAD_REQUEST: -+ mip6_fn.icmpv6_dhaad_req_rcv(skb); -+ break; -+ -+ case MIPV6_PREFIX_SOLICIT: -+ mip6_fn.icmpv6_pfxsol_rcv(skb); -+ break; -+ } -+} -+ -+int mipv6_icmpv6_init(void) -+{ -+ struct sock *sk; -+ int err; -+ -+ if ((mipv6_icmpv6_socket = sock_alloc()) == NULL) { -+ DEBUG(DBG_ERROR, "Cannot allocate mipv6_icmpv6_socket"); -+ return -1; -+ } -+ mipv6_icmpv6_socket->type = SOCK_RAW; -+ -+ if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_ICMP, -+ &mipv6_icmpv6_socket)) < 0) { -+ DEBUG(DBG_ERROR, "Cannot initialize mipv6_icmpv6_socket"); -+ sock_release(mipv6_icmpv6_socket); -+ mipv6_icmpv6_socket = NULL; /* For safety */ -+ return err; -+ } -+ sk = mipv6_icmpv6_socket->sk; -+ sk->allocation = GFP_ATOMIC; -+ sk->prot->unhash(sk); -+ -+ /* Register our ICMP handler */ -+ MIPV6_SETCALL(mipv6_icmp_rcv, icmp6_rcv); -+ return 0; -+} -+ -+void mipv6_icmpv6_exit(void) -+{ -+ MIPV6_RESETCALL(mipv6_icmp_rcv); -+ if (mipv6_icmpv6_socket) -+ sock_release(mipv6_icmpv6_socket); -+ mipv6_icmpv6_socket = NULL; /* For safety */ -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp.h -@@ -0,0 +1,43 @@ -+/* -+ * MIPL Mobile IPv6 ICMP send and receive prototypes -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MIPV6_ICMP -+#define _MIPV6_ICMP -+ -+#include <linux/config.h> -+#include <linux/in6.h> -+ -+void mipv6_icmpv6_send(struct in6_addr *daddr, struct in6_addr *saddr, -+ int type, int code, __u16 *id, __u16 flags, -+ void *data, int datalen); -+ -+void mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id); -+ -+void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr); -+/* No handling */ -+int mipv6_icmpv6_no_rcv(struct sk_buff *skb); -+ -+/* Receive DHAAD Reply message */ -+int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb); -+/* Receive Parameter Problem message */ -+int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb); -+/* Receive prefix advertisements */ -+int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb); -+ -+/* Receive DHAAD Request message */ -+int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb); -+/* Receive prefix solicitations */ -+int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb); -+ -+int mipv6_icmpv6_init(void); -+void mipv6_icmpv6_exit(void); -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp_ha.c -@@ -0,0 +1,158 @@ -+/* -+ * Home Agent specific ICMP routines -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "halist.h" -+#include "debug.h" -+#include "mipv6_icmp.h" -+//#include "prefix.h" -+ -+/* Is this the easiest way of checking on -+ * which interface an anycast address is ? -+ */ -+static int find_ac_dev(struct in6_addr *addr) -+{ -+ int ifindex = 0; -+ struct net_device *dev; -+ read_lock(&dev_base_lock); -+ for (dev=dev_base; dev; dev=dev->next) { -+ if (ipv6_chk_acast_addr(dev, addr)) { -+ ifindex = dev->ifindex; -+ break; -+ } -+ } -+ read_unlock(&dev_base_lock); -+ return ifindex; -+} -+ -+/** -+ * mipv6_icmpv6_send_dhaad_rep - Reply to DHAAD Request -+ * @ifindex: index of interface request was received from -+ * @id: request's identification number -+ * @daddr: requester's IPv6 address -+ * -+ * When Home Agent receives Dynamic Home Agent Address Discovery -+ * request, it replies with a list of home agents available on the -+ * home link. -+ */ -+void mipv6_icmpv6_send_dhaad_rep(int ifindex, __u16 id, struct in6_addr *daddr) -+{ -+ __u8 *data = NULL; -+ struct in6_addr home, *ha_addrs = NULL; -+ int addr_count, max_addrs, size = 0; -+ -+ if (daddr == NULL) -+ return; -+ -+ if (mipv6_ha_get_addr(ifindex, &home) < 0) { -+ DEBUG(DBG_INFO, "Not Home Agent in this interface"); -+ return; -+ } -+ -+ /* We send all available HA addresses, not exceeding a maximum -+ * number we can fit in a packet with minimum IPv6 MTU (to -+ * avoid fragmentation). -+ */ -+ max_addrs = 76; -+ addr_count = mipv6_ha_get_pref_list(ifindex, &ha_addrs, max_addrs); -+ -+ if (addr_count < 0) return; -+ -+ if (addr_count != 0 && ha_addrs == NULL) { -+ DEBUG(DBG_ERROR, "addr_count = %d but return no addresses", -+ addr_count); -+ return; -+ } -+ data = (u8 *)ha_addrs; -+ -+ size = addr_count * sizeof(struct in6_addr); -+ -+ mipv6_icmpv6_send(daddr, &home, MIPV6_DHAAD_REPLY, -+ 0, &id, 0, data, size); -+ if (ha_addrs) { -+ data = NULL; -+ kfree(ha_addrs); -+ } -+} -+ -+/** -+ * mipv6_icmpv6_dhaad_req - Home Agent Address Discovery Request ICMP handler -+ * @skb: buffer containing ICMP information message -+ * -+ * Special Mobile IPv6 ICMP message. Handles Dynamic Home Agent -+ * Address Discovery Request messages. -+ **/ -+int mipv6_icmpv6_rcv_dhaad_req(struct sk_buff *skb) -+{ -+ struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr; -+ __u16 identifier; -+ int ifindex = 0; -+ -+ DEBUG_FUNC(); -+ -+ /* Invalid packet checks. */ -+ if (phdr->icmp6_code != 0) -+ return 0; -+ -+ identifier = ntohs(phdr->icmp6_identifier); -+ -+ /* -+ * Make sure we have the right ifindex (if the -+ * req came through another interface. -+ */ -+ ifindex = find_ac_dev(daddr); -+ if (ifindex == 0) { -+ DEBUG(DBG_WARNING, "received dhaad request to anycast address %x:%x:%x:%x:%x:%x:%x:%x" -+ " on which prefix we are not HA", -+ NIPV6ADDR(daddr)); -+ return 0; -+ } -+ -+ /* -+ * send reply with list -+ */ -+ mipv6_icmpv6_send_dhaad_rep(ifindex, identifier, saddr); -+ return 1; -+} -+#if 0 -+/** -+ * mipv6_icmpv6_handle_pfx_sol - handle prefix solicitations -+ * @skb: sk_buff including the icmp6 message -+ */ -+int mipv6_icmpv6_rcv_pfx_sol(struct sk_buff *skb) -+{ -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr; -+ struct inet6_ifaddr *ifp; -+ -+ DEBUG_FUNC(); -+ -+ if (!(ifp = ipv6_get_ifaddr(daddr, NULL))) -+ return -1; -+ -+ in6_ifa_put(ifp); -+ mipv6_pfx_cancel_send(saddr, -1); -+ -+ return 0; -+} -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mipv6_icmp_mn.c -@@ -0,0 +1,273 @@ -+/* -+ * Mobile Node specific ICMP routines -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/sched.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "mn.h" -+#include "bul.h" -+#include "mdetect.h" -+#include "debug.h" -+#include "mipv6_icmp.h" -+#include "util.h" -+//#include "prefix.h" -+ -+#define INFINITY 0xffffffff -+ -+/** -+ * mipv6_icmpv6_paramprob - Parameter Problem ICMP error message handler -+ * @skb: buffer containing ICMP error message -+ * -+ * Special Mobile IPv6 ICMP handling. If Mobile Node receives ICMP -+ * Parameter Problem message when using a Home Address Option, -+ * offending node should be logged and error message dropped. If -+ * error is received because of a Binding Update, offending node -+ * should be recorded in Binding Update List and no more Binding -+ * Updates should be sent to this destination. See RFC 3775 section -+ * 10.15. -+ **/ -+int mipv6_icmpv6_rcv_paramprob(struct sk_buff *skb) -+{ -+ struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *saddr = skb ? &skb->nh.ipv6h->saddr : NULL; -+ struct in6_addr *daddr = skb ? &skb->nh.ipv6h->daddr : NULL; -+ struct ipv6hdr *hdr = (struct ipv6hdr *) (phdr + 1); -+ int ulen = (skb->tail - (unsigned char *) (phdr + 1)); -+ -+ int errptr; -+ __u8 *off_octet; -+ -+ DEBUG_FUNC(); -+ -+ /* We only handle code 1 & 2 messages. */ -+ if (phdr->icmp6_code != ICMPV6_UNK_NEXTHDR && -+ phdr->icmp6_code != ICMPV6_UNK_OPTION) -+ return 0; -+ -+ /* Find offending octet in the original packet. */ -+ errptr = ntohl(phdr->icmp6_pointer); -+ -+ /* There is not enough of the original packet left to figure -+ * out what went wrong. Bail out. */ -+ if (ulen <= errptr) -+ return 0; -+ -+ off_octet = ((__u8 *) hdr + errptr); -+ DEBUG(DBG_INFO, "Parameter problem: offending octet %d [0x%2x]", -+ errptr, *off_octet); -+ -+ /* If CN did not understand Mobility Header, set BUL entry to -+ * ACK_ERROR so no further BUs are sumbitted to this CN. */ -+ if (phdr->icmp6_code == ICMPV6_UNK_NEXTHDR && -+ *off_octet == IPPROTO_MOBILITY) { -+ struct bul_inval_args args; -+ args.all_rr_states = 1; -+ args.cn = saddr; -+ args.mn = daddr; -+ write_lock(&bul_lock); -+ mipv6_bul_iterate(mn_bul_invalidate, &args); -+ write_unlock(&bul_lock); -+ } -+ -+ /* If CN did not understand Home Address Option, we log an -+ * error and discard the error message. */ -+ if (phdr->icmp6_code == ICMPV6_UNK_OPTION && -+ *off_octet == MIPV6_TLV_HOMEADDR) { -+ DEBUG(DBG_WARNING, "Correspondent node does not " -+ "implement Home Address Option receipt."); -+ return 1; -+ } -+ return 0; -+} -+ -+/** -+ * mipv6_mn_dhaad_send_req - Send DHAAD Request to home network -+ * @home_addr: address to do DHAAD for -+ * @plen: prefix length for @home_addr -+ * -+ * Send Dynamic Home Agent Address Discovery Request to the Home -+ * Agents anycast address in the nodes home network. -+ **/ -+void -+mipv6_icmpv6_send_dhaad_req(struct in6_addr *home_addr, int plen, __u16 dhaad_id) -+{ -+ struct in6_addr ha_anycast; -+ struct in6_addr careofaddr; -+ -+ if (mipv6_get_care_of_address(home_addr, &careofaddr) < 0) { -+ DEBUG(DBG_WARNING, "Could not get node's Care-of Address"); -+ return; -+ } -+ -+ if (mipv6_ha_anycast(&ha_anycast, home_addr, plen) < 0) { -+ DEBUG(DBG_WARNING, -+ "Could not get Home Agent Anycast address for home address %x:%x.%x:%x:%x:%x:%x:%x/%d", -+ NIPV6ADDR(home_addr), plen); -+ return; -+ } -+ -+ mipv6_icmpv6_send(&ha_anycast, &careofaddr, MIPV6_DHAAD_REQUEST, 0, -+ &dhaad_id, 0, NULL, 0); -+ -+} -+ -+/** -+ * mipv6_icmpv6_dhaad_rep - Home Agent Address Discovery Reply ICMP handler -+ * @skb: buffer containing ICMP information message -+ * -+ * Special Mobile IPv6 ICMP message. Handles Dynamic Home Agent -+ * Address Discovery Reply messages. -+ **/ -+int mipv6_icmpv6_rcv_dhaad_rep(struct sk_buff *skb) -+{ -+ struct icmp6hdr *phdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *address; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ __u16 identifier; -+ int ulen = (skb->tail - (unsigned char *) ((__u32 *) phdr + 2)); -+ int i; -+ struct in6_addr home_addr, coa; -+ struct in6_addr *first_ha = NULL; -+ struct mn_info *minfo; -+ int n_addr = ulen / sizeof(struct in6_addr); -+ -+ DEBUG_FUNC(); -+ -+ /* Invalid packet checks. */ -+ if (ulen % sizeof(struct in6_addr) != 0) -+ return 0; -+ -+ if (phdr->icmp6_code != 0) -+ return 0; -+ -+ identifier = ntohs(phdr->icmp6_identifier); -+ if (ulen > 0) { -+ address = (struct in6_addr *) ((__u32 *) phdr + 2); -+ } else { -+ address = saddr; -+ n_addr = 1; -+ } -+ -+ /* receive list of home agent addresses -+ * add to home agents list -+ */ -+ DEBUG(DBG_INFO, "DHAAD: got %d home agents", n_addr); -+ -+ first_ha = address; -+ -+ /* lookup H@ with identifier */ -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_id(identifier); -+ if (!minfo) { -+ read_unlock(&mn_info_lock); -+ DEBUG(DBG_INFO, "no mninfo with id %d", -+ identifier); -+ return 0; -+ } -+ spin_lock(&minfo->lock); -+ -+ /* Logic: -+ * 1. if old HA on list, prefer it -+ * 2. otherwise first HA on list prefered -+ */ -+ for (i = 0; i < n_addr; i++) { -+ DEBUG(DBG_INFO, "HA[%d] %x:%x:%x:%x:%x:%x:%x:%x", -+ i, NIPV6ADDR(address)); -+ if (ipv6_addr_cmp(&minfo->ha, address) == 0) { -+ spin_unlock(&minfo->lock); -+ read_unlock(&mn_info_lock); -+ return 0; -+ } -+ address++; -+ } -+ ipv6_addr_copy(&minfo->ha, first_ha); -+ spin_unlock(&minfo->lock); -+ ipv6_addr_copy(&home_addr, &minfo->home_addr); -+ read_unlock(&mn_info_lock); -+ -+ mipv6_get_care_of_address(&home_addr, &coa); -+ init_home_registration(&home_addr, &coa); -+ -+ return 1; -+} -+#if 0 -+/** -+ * mipv6_icmpv6_handle_pfx_adv - handle prefix advertisements -+ * @skb: sk_buff including the icmp6 message -+ */ -+int mipv6_icmpv6_rcv_pfx_adv(struct sk_buff *skb) -+{ -+ struct icmp6hdr *hdr = (struct icmp6hdr *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr *daddr = &skb->nh.ipv6h->daddr; -+ __u8 *opt = (__u8 *) (hdr + 1); -+ int optlen = (skb->tail - opt); -+ unsigned long min_expire = INFINITY; -+ struct inet6_skb_parm *parm = (struct inet6_skb_parm *) skb->cb; -+ -+ DEBUG_FUNC(); -+ -+ while (optlen > 0) { -+ int len = opt[1] << 3; -+ if (len == 0) -+ goto set_timer; -+ -+ if (opt[0] == ND_OPT_PREFIX_INFO) { -+ int ifindex; -+ unsigned long expire; -+ struct prefix_info *pinfo = -+ (struct prefix_info *) opt; -+ struct net_device *dev; -+ struct mn_info *mninfo; -+ -+ read_lock(&mn_info_lock); -+ mninfo = mipv6_mninfo_get_by_ha(saddr); -+ if (mninfo == NULL) { -+ ifindex = 0; -+ } else { -+ spin_lock(&mninfo->lock); -+ ifindex = mninfo->ifindex; -+ spin_unlock(&mninfo->lock); -+ mninfo = NULL; -+ } -+ read_unlock(&mn_info_lock); -+ -+ if (!(dev = dev_get_by_index(ifindex))) { -+ DEBUG(DBG_WARNING, "Cannot find device by index %d", parm->iif); -+ goto nextopt; -+ } -+ -+ expire = ntohl(pinfo->valid); -+ expire = expire == 0 ? INFINITY : expire; -+ -+ min_expire = expire < min_expire ? expire : min_expire; -+ -+ dev_put(dev); -+ } -+ -+nextopt: -+ optlen -= len; -+ opt += len; -+ } -+ -+set_timer: -+ -+ mipv6_pfx_add_home(parm->iif, saddr, daddr, min_expire); -+ return 0; -+} -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mn.c -@@ -0,0 +1,1521 @@ -+/* -+ * Mobile-node functionality -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/sched.h> -+#include <linux/ipv6.h> -+#include <linux/net.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/rtnetlink.h> -+#include <linux/if_arp.h> -+#include <linux/ipsec.h> -+#include <linux/notifier.h> -+#include <linux/list.h> -+#include <linux/route.h> -+#include <linux/netfilter.h> -+#include <linux/netfilter_ipv6.h> -+#include <linux/tqueue.h> -+#include <linux/proc_fs.h> -+ -+#include <asm/uaccess.h> -+ -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/neighbour.h> -+#include <net/ndisc.h> -+#include <net/ip6_route.h> -+#include <net/mipglue.h> -+ -+#include "util.h" -+#include "mdetect.h" -+#include "bul.h" -+#include "mobhdr.h" -+#include "debug.h" -+#include "mn.h" -+#include "mipv6_icmp.h" -+#include "multiaccess_ctl.h" -+//#include "prefix.h" -+#include "tunnel_mn.h" -+#include "stats.h" -+#include "config.h" -+ -+#define MIPV6_BUL_SIZE 128 -+ -+static LIST_HEAD(mn_info_list); -+ -+/* Lock for list of MN infos */ -+rwlock_t mn_info_lock = RW_LOCK_UNLOCKED; -+ -+static spinlock_t ifrh_lock = SPIN_LOCK_UNLOCKED; -+ -+struct ifr_holder { -+ struct list_head list; -+ struct in6_ifreq ifr; -+ int old_ifi; -+ struct handoff *ho; -+}; -+ -+LIST_HEAD(ifrh_list); -+ -+static struct tq_struct mv_home_addr_task; -+ -+/* Determines whether manually configured home addresses are preferred as -+ * source addresses over dynamically configured ones -+ */ -+int mipv6_use_preconfigured_hoaddr = 1; -+ -+/* Determines whether home addresses, which are at home are preferred as -+ * source addresses over other home addresses -+ */ -+int mipv6_use_topol_corr_hoaddr = 0; -+ -+static spinlock_t icmpv6_id_lock = SPIN_LOCK_UNLOCKED; -+static __u16 icmpv6_id = 0; -+ -+static inline __u16 mipv6_get_dhaad_id(void) -+{ -+ __u16 ret; -+ spin_lock_bh(&icmpv6_id_lock); -+ ret = ++icmpv6_id; -+ spin_unlock_bh(&icmpv6_id_lock); -+ return ret; -+} -+ -+/** -+ * mipv6_mninfo_get_by_home - Returns mn_info for a home address -+ * @haddr: home address of MN -+ * -+ * Returns mn_info on success %NULL otherwise. Caller MUST hold -+ * @mn_info_lock (read or write). -+ **/ -+struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo; -+ -+ DEBUG_FUNC(); -+ -+ if (!haddr) -+ return NULL; -+ -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (!ipv6_addr_cmp(&minfo->home_addr, haddr)) { -+ spin_unlock(&minfo->lock); -+ return minfo; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ return NULL; -+} -+ -+/** -+ * mipv6_mninfo_get_by_ha - Lookup mn_info with Home Agent address -+ * @home_agent: Home Agent address -+ * -+ * Searches for a mn_info entry with @ha set to @home_agent. You MUST -+ * hold @mn_info_lock when calling this function. Returns pointer to -+ * mn_info entry or %NULL on failure. -+ **/ -+struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo; -+ -+ if (!home_agent) -+ return NULL; -+ -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (!ipv6_addr_cmp(&minfo->ha, home_agent)) { -+ spin_unlock(&minfo->lock); -+ return minfo; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ return NULL; -+} -+ -+/** -+ * mipv6_mninfo_get_by_id - Lookup mn_info with id -+ * @id: DHAAD identifier -+ * -+ * Searches for a mn_info entry with @dhaad_id set to @id. You MUST -+ * hold @mn_info_lock when calling this function. Returns pointer to -+ * mn_info entry or %NULL on failure. -+ **/ -+struct mn_info *mipv6_mninfo_get_by_id(unsigned short id) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo = 0; -+ -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (minfo->dhaad_id == id) { -+ spin_unlock(&minfo->lock); -+ return minfo; -+ } -+ spin_unlock(&minfo->lock); -+ } -+ return NULL; -+} -+ -+/** -+ * mipv6_mninfo_add - Adds a new home info for MN -+ * @ifindex: Interface for home address -+ * @home_addr: Home address of MN, must be set -+ * @plen: prefix length of the home address, must be set -+ * @isathome : home address at home -+ * @lifetime: lifetime of the home address, 0 is infinite -+ * @ha: home agent for the home address -+ * @ha_plen: prefix length of home agent's address, can be zero -+ * @ha_lifetime: Lifetime of the home address, 0 is infinite -+ * -+ * The function adds a new home info entry for MN, allowing it to -+ * register the home address with the home agent. Starts home -+ * registration process. If @ha is %ADDRANY, DHAAD is performed to -+ * find a home agent. Returns 0 on success, a negative value -+ * otherwise. Caller MUST NOT hold @mn_info_lock or -+ * @addrconf_hash_lock. -+ **/ -+void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, -+ int isathome, unsigned long lifetime, struct in6_addr *ha, -+ int ha_plen, unsigned long ha_lifetime, int man_conf) -+{ -+ struct mn_info *minfo; -+ struct in6_addr coa; -+ -+ DEBUG_FUNC(); -+ -+ write_lock_bh(&mn_info_lock); -+ if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL){ -+ DEBUG(1, "MN info already exists"); -+ write_unlock_bh(&mn_info_lock); -+ return; -+ } -+ minfo = kmalloc(sizeof(struct mn_info), GFP_ATOMIC); -+ if (!minfo) { -+ write_unlock_bh(&mn_info_lock); -+ return; -+ } -+ memset(minfo, 0, sizeof(struct mn_info)); -+ spin_lock_init(&minfo->lock); -+ -+ -+ ipv6_addr_copy(&minfo->home_addr, home_addr); -+ -+ if (ha) -+ ipv6_addr_copy(&minfo->ha, ha); -+ if (ha_plen < 128 && ha_plen > 0) -+ minfo->home_plen = ha_plen; -+ else minfo->home_plen = 64; -+ -+ minfo->ifindex_user = ifindex; /* Ifindex for tunnel interface */ -+ minfo->ifindex = ifindex; /* Interface on which home address is currently conf'd */ -+ /* TODO: we should get home address lifetime from somewhere */ -+ /* minfo->home_addr_expires = jiffies + lifetime * HZ; */ -+ -+ /* manual configuration flag cannot be unset by dynamic updates -+ * from prefix advertisements -+ */ -+ if (!minfo->man_conf) minfo->man_conf = man_conf; -+ minfo->is_at_home = isathome; -+ -+ list_add(&minfo->list, &mn_info_list); -+ write_unlock_bh(&mn_info_lock); -+ -+ if (mipv6_get_care_of_address(home_addr, &coa) == 0) -+ init_home_registration(home_addr, &coa); -+} -+ -+/** -+ * mipv6_mninfo_del - Delete home info for MN -+ * @home_addr : Home address or prefix -+ * @del_dyn_only : Delete only dynamically created home entries -+ * -+ * Deletes every mn_info entry that matches the first plen bits of -+ * @home_addr. Returns number of deleted entries on success and a -+ * negative value otherwise. Caller MUST NOT hold @mn_info_lock. -+ **/ -+int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only) -+{ -+ struct list_head *lh, *next; -+ struct mn_info *minfo; -+ int ret = -1; -+ if (!home_addr) -+ return -1; -+ -+ write_lock(&mn_info_lock); -+ -+ list_for_each_safe(lh, next, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ if (ipv6_addr_cmp(&minfo->home_addr, home_addr) == 0 -+ && ((!minfo->man_conf && del_dyn_only) || !del_dyn_only)){ -+ list_del(&minfo->list); -+ kfree(minfo); -+ ret++; -+ } -+ } -+ write_unlock(&mn_info_lock); -+ return ret; -+} -+ -+void mipv6_mn_set_home(int ifindex, struct in6_addr *homeaddr, int plen, -+ struct in6_addr *homeagent, int ha_plen) -+{ -+ mipv6_mninfo_add(ifindex, homeaddr, plen, 0, 0, -+ homeagent, ha_plen, 0, 1); -+} -+ -+static int skip_dad(struct in6_addr *addr) -+{ -+ struct mn_info *minfo; -+ int ret = 0; -+ -+ if (addr == NULL) { -+ DEBUG(DBG_CRITICAL, "Null argument"); -+ return 0; -+ } -+ read_lock_bh(&mn_info_lock); -+ if ((minfo = mipv6_mninfo_get_by_home(addr)) != NULL) { -+ if ((minfo->is_at_home != MN_NOT_AT_HOME) && (minfo->has_home_reg)) -+ ret = 1; -+ DEBUG(DBG_INFO, "minfo->is_at_home = %d, minfo->has_home_reg = %d", -+ minfo->is_at_home, minfo->has_home_reg); -+ } -+ read_unlock_bh(&mn_info_lock); -+ -+ return ret; -+} -+/** -+ * mipv6_mn_is_home_addr - Determines if addr is node's home address -+ * @addr: IPv6 address -+ * -+ * Returns 1 if addr is node's home address. Otherwise returns zero. -+ **/ -+int mipv6_mn_is_home_addr(struct in6_addr *addr) -+{ -+ int ret = 0; -+ -+ if (addr == NULL) { -+ DEBUG(DBG_CRITICAL, "Null argument"); -+ return -1; -+ } -+ read_lock_bh(&mn_info_lock); -+ if (mipv6_mninfo_get_by_home(addr)) -+ ret = 1; -+ read_unlock_bh(&mn_info_lock); -+ -+ return (ret); -+} -+ -+/** -+ * mipv6_mn_is_at_home - determine if node is home for a home address -+ * @home_addr : home address of MN -+ * -+ * Returns 1 if home address in question is in the home network, 0 -+ * otherwise. Caller MUST NOT not hold @mn_info_lock. -+ **/ -+int mipv6_mn_is_at_home(struct in6_addr *home_addr) -+{ -+ struct mn_info *minfo; -+ int ret = 0; -+ read_lock_bh(&mn_info_lock); -+ if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) { -+ spin_lock(&minfo->lock); -+ ret = (minfo->is_at_home == MN_AT_HOME); -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+ return ret; -+} -+void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg) -+{ -+ struct mn_info *minfo; -+ read_lock_bh(&mn_info_lock); -+ -+ if ((minfo = mipv6_mninfo_get_by_home(home_addr)) != NULL) { -+ spin_lock(&minfo->lock); -+ minfo->has_home_reg = has_home_reg; -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+} -+ -+static int mn_inet6addr_event( -+ struct notifier_block *nb, unsigned long event, void *ptr) -+{ -+ struct inet6_ifaddr *ifp = (struct inet6_ifaddr *)ptr; -+ -+ switch (event) { -+ case NETDEV_UP: -+ /* Is address a valid coa ?*/ -+ if (!(ifp->flags & IFA_F_TENTATIVE)) -+ mipv6_mdet_finalize_ho(&ifp->addr, -+ ifp->idev->dev->ifindex); -+ else if(skip_dad(&ifp->addr)) -+ ifp->flags &= ~IFA_F_TENTATIVE; -+ break; -+ case NETDEV_DOWN: -+#if 0 -+ /* This is useless with manually configured home -+ addresses, which will not expire -+ */ -+ mipv6_mninfo_del(&ifp->addr, 0); -+#endif -+ break; -+ -+ } -+ -+ return NOTIFY_DONE; -+} -+ -+struct notifier_block mipv6_mn_inet6addr_notifier = { -+ mn_inet6addr_event, -+ NULL, -+ 0 /* check if using zero is ok */ -+}; -+ -+static void mipv6_get_saddr_hook(struct in6_addr *homeaddr) -+{ -+ int found = 0, reiter = 0; -+ struct list_head *lh; -+ struct mn_info *minfo = NULL; -+ struct in6_addr coa; -+ -+ read_lock_bh(&mn_info_lock); -+restart: -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ if ((ipv6_addr_scope(homeaddr) != ipv6_addr_scope(&minfo->home_addr)) -+ || ipv6_chk_addr(&minfo->home_addr, NULL) == 0) -+ continue; -+ -+ spin_lock(&minfo->lock); -+ if (minfo->is_at_home == MN_AT_HOME || minfo->has_home_reg) { -+ if ((mipv6_use_topol_corr_hoaddr && -+ minfo->is_at_home == MN_AT_HOME) || -+ (mipv6_use_preconfigured_hoaddr && -+ minfo->man_conf) || -+ (!(mipv6_use_preconfigured_hoaddr || -+ mipv6_use_topol_corr_hoaddr) || reiter)) { -+ spin_unlock(&minfo->lock); -+ ipv6_addr_copy(homeaddr, &minfo->home_addr); -+ found = 1; -+ break; -+ } -+ } -+ spin_unlock(&minfo->lock); -+ } -+ if (!found && !reiter) { -+ reiter = 1; -+ goto restart; -+ } -+ -+ if (!found && minfo && -+ !mipv6_get_care_of_address(&minfo->home_addr, &coa)) { -+ ipv6_addr_copy(homeaddr, &coa); -+ } -+ read_unlock_bh(&mn_info_lock); -+ -+ DEBUG(DBG_DATADUMP, "Source address selection: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(homeaddr)); -+ return; -+} -+ -+static void mv_home_addr(void *arg) -+{ -+ mm_segment_t oldfs; -+ int err = 0, new_if = 0; -+ struct list_head *lh, *next; -+ struct ifr_holder *ifrh; -+ LIST_HEAD(list); -+ -+ DEBUG(DBG_INFO, "mipv6 move home address task"); -+ -+ spin_lock_bh(&ifrh_lock); -+ list_splice_init(&ifrh_list, &list); -+ spin_unlock_bh(&ifrh_lock); -+ -+ oldfs = get_fs(); set_fs(KERNEL_DS); -+ list_for_each_safe(lh, next, &list) { -+ ifrh = list_entry(lh, struct ifr_holder, list); -+ if (ifrh->old_ifi) { -+ new_if = ifrh->ifr.ifr6_ifindex; -+ ifrh->ifr.ifr6_ifindex = ifrh->old_ifi; -+ err = addrconf_del_ifaddr(&ifrh->ifr); -+ ifrh->ifr.ifr6_ifindex = new_if; -+ if (err < 0) -+ DEBUG(DBG_WARNING, "removal of home address %x:%x:%x:%x:%x:%x:%x:%x from" -+ " old interface %d failed with status %d", -+ NIPV6ADDR(&ifrh->ifr.ifr6_addr), ifrh->old_ifi, err); -+ } -+ if(!err) { -+ err = addrconf_add_ifaddr(&ifrh->ifr); -+ } -+ if (ifrh->ho) { -+ DEBUG(DBG_INFO, "Calling mobile_node moved after moving home address to new if"); -+ mipv6_mobile_node_moved(ifrh->ho); -+ } -+ list_del(&ifrh->list); -+ kfree(ifrh); -+ } -+ set_fs(oldfs); -+ -+ if (err < 0) -+ DEBUG(DBG_WARNING, "adding of home address to a new interface %d failed %d", new_if, err); -+ else { -+ DEBUG(DBG_WARNING, "adding of home address to a new interface OK"); -+ } -+} -+ -+struct dhaad_halist { -+ struct list_head list; -+ struct in6_addr addr; -+ int retry; -+}; -+ -+/* clear all has from candidate list. do this when a new dhaad reply -+ * is received. */ -+int mipv6_mn_flush_ha_candidate(struct list_head *ha) -+{ -+ struct list_head *p, *tmp; -+ struct dhaad_halist *e; -+ -+ list_for_each_safe(p, tmp, ha) { -+ e = list_entry(p, struct dhaad_halist, list); -+ list_del(p); -+ kfree(e); -+ e = NULL; -+ } -+ return 0; -+} -+ -+/* add new ha to candidates. only done when dhaad reply is received. */ -+int mipv6_mn_add_ha_candidate(struct list_head *ha, struct in6_addr *addr) -+{ -+ struct dhaad_halist *e; -+ -+ e = kmalloc(sizeof(*e), GFP_ATOMIC); -+ memset(e, 0, sizeof(*e)); -+ ipv6_addr_copy(&e->addr, addr); -+ -+ list_add_tail(&e->list, ha); -+ return 0; -+} -+ -+#define MAX_RETRIES_PER_HA 3 -+ -+/* get next ha candidate. this is done when dhaad reply has been -+ * received and we want to register with the best available ha. */ -+int mipv6_mn_get_ha_candidate(struct list_head *ha, struct in6_addr *addr) -+{ -+ struct list_head *p; -+ -+ list_for_each(p, ha) { -+ struct dhaad_halist *e; -+ e = list_entry(p, typeof(*e), list); -+ if (e->retry >= 0 && e->retry < MAX_RETRIES_PER_HA) { -+ ipv6_addr_copy(addr, &e->addr); -+ return 0; -+ } -+ } -+ return -1; -+} -+ -+/* change candidate status. if registration with ha fails, we -+ * increase retry for ha candidate. if retry is >= 3 we set it to -1 -+ * (failed), do get_ha_candidate() again */ -+int mipv6_mn_try_ha_candidate(struct list_head *ha, struct in6_addr *addr) -+{ -+ struct list_head *p; -+ -+ list_for_each(p, ha) { -+ struct dhaad_halist *e; -+ e = list_entry(p, typeof(*e), list); -+ if (ipv6_addr_cmp(addr, &e->addr) == 0) { -+ if (e->retry >= MAX_RETRIES_PER_HA) e->retry = -1; -+ else if (e->retry >= 0) e->retry++; -+ return 0; -+ } -+ } -+ return -1; -+} -+ -+/** -+ * mipv6_mn_get_bulifetime - Get lifetime for a binding update -+ * @home_addr: home address for BU -+ * @coa: care-of address for BU -+ * @flags: flags used for BU -+ * -+ * Returns maximum lifetime for BUs determined by the lifetime of -+ * care-of address and the lifetime of home address. -+ **/ -+__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, struct in6_addr *coa, -+ __u8 flags) -+{ -+ struct inet6_ifaddr *ifp_hoa, *ifp_coa; -+ __u32 lifetime = (flags & MIPV6_BU_F_HOME ? -+ HA_BU_DEF_LIFETIME : CN_BU_DEF_LIFETIME); -+ -+ ifp_hoa = ipv6_get_ifaddr(home_addr, NULL); -+ if(!ifp_hoa) { -+ DEBUG(DBG_INFO, "home address missing"); -+ return 0; -+ } -+ if (!(ifp_hoa->flags & IFA_F_PERMANENT)){ -+ if (ifp_hoa->valid_lft) -+ lifetime = min_t(__u32, lifetime, ifp_hoa->valid_lft); -+ else -+ DEBUG(DBG_ERROR, "Zero lifetime for home address"); -+ } -+ in6_ifa_put(ifp_hoa); -+ -+ ifp_coa = ipv6_get_ifaddr(coa, NULL); -+ if (!ifp_coa) { -+ DEBUG(DBG_INFO, "care-of address missing"); -+ return 0; -+ } -+ if (!(ifp_coa->flags & IFA_F_PERMANENT)) { -+ if(ifp_coa->valid_lft) -+ lifetime = min_t(__u32, lifetime, ifp_coa->valid_lft); -+ else -+ DEBUG(DBG_ERROR, -+ "Zero lifetime for care-of address"); -+ } -+ in6_ifa_put(ifp_coa); -+ -+ DEBUG(DBG_INFO, "Lifetime for binding is %ld", lifetime); -+ return lifetime; -+} -+ -+static int -+mipv6_mn_tnl_rcv_send_bu_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ struct ipv6hdr *inner; -+ struct ipv6hdr *outer = skb->nh.ipv6h; -+ struct mn_info *minfo = NULL; -+ __u32 lifetime; -+ __u8 user_flags = 0; -+ -+ DEBUG_FUNC(); -+ -+ if (!is_mip6_tnl(t)) -+ return IP6_TNL_ACCEPT; -+ -+ if (!mip6node_cnf.accept_ret_rout) { -+ DEBUG(DBG_INFO, "Return routability administratively disabled" -+ " not doing route optimization"); -+ return IP6_TNL_ACCEPT; -+ } -+ if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*inner))) -+ return IP6_TNL_DROP; -+ -+ inner = (struct ipv6hdr *)skb->h.raw; -+ -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_home(&inner->daddr); -+ -+ if (!minfo) { -+ DEBUG(DBG_WARNING, "MN info missing"); -+ read_unlock(&mn_info_lock); -+ return IP6_TNL_ACCEPT; -+ } -+ DEBUG(DBG_DATADUMP, "MIPV6 MN: Received a tunneled IPv6 packet" -+ " to %x:%x:%x:%x:%x:%x:%x:%x," -+ " from %x:%x:%x:%x:%x:%x:%x:%x with\n tunnel header" -+ "daddr: %x:%x:%x:%x:%x:%x:%x:%x," -+ "saddr: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&inner->daddr), NIPV6ADDR(&inner->saddr), -+ NIPV6ADDR(&outer->daddr), NIPV6ADDR(&outer->saddr)); -+ -+ spin_lock(&minfo->lock); -+ -+ /* We don't send bus in response to all tunneled packets */ -+ -+ if (!ipv6_addr_cmp(&minfo->ha, &inner->saddr)) { -+ spin_unlock(&minfo->lock); -+ read_unlock(&mn_info_lock); -+ DEBUG(DBG_ERROR, "HA BUG: Received a tunneled packet " -+ "originally sent by home agent, not sending BU"); -+ return IP6_TNL_ACCEPT; -+ } -+ spin_unlock(&minfo->lock); -+ read_unlock(&mn_info_lock); -+ -+ DEBUG(DBG_DATADUMP, "Sending BU to correspondent node"); -+ -+ user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0; -+ -+ if (inner->nexthdr != IPPROTO_DSTOPTS && -+ inner->nexthdr != IPPROTO_MOBILITY) { -+ struct in6_addr coa; -+ /* Don't start RR when receiving ICMP error messages */ -+ if (inner->nexthdr == IPPROTO_ICMPV6) { -+ int ptr = (u8*)(inner+1) - skb->data; -+ u8 type; -+ -+ if (skb_copy_bits(skb, -+ ptr+offsetof(struct icmp6hdr, -+ icmp6_type), -+ &type, 1) -+ || !(type & ICMPV6_INFOMSG_MASK)) { -+ return IP6_TNL_ACCEPT; -+ } -+ } -+ lifetime = mipv6_mn_get_bulifetime(&inner->daddr, -+ &outer->daddr, 0); -+ if (lifetime && -+ !mipv6_get_care_of_address(&inner->daddr, &coa)) { -+ write_lock(&bul_lock); -+ mipv6_send_bu(&inner->daddr, &inner->saddr, &coa, -+ INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, -+ user_flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ } -+ } -+ DEBUG(DBG_DATADUMP, "setting rcv_tunnel flag in skb"); -+ skb->security |= MIPV6_RCV_TUNNEL; -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_send_bu_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_DECAP, -+ IP6_TNL_PRI_FIRST, -+ mipv6_mn_tnl_rcv_send_bu_hook -+}; -+ -+static int -+mipv6_mn_tnl_xmit_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_encapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_mn_tnl_xmit_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_ENCAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_mn_tnl_xmit_stats_hook -+}; -+ -+static int -+mipv6_mn_tnl_rcv_stats_hook(struct ip6_tnl *t, struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ if (is_mip6_tnl(t)) -+ MIPV6_INC_STATS(n_decapsulations); -+ return IP6_TNL_ACCEPT; -+} -+ -+static struct ip6_tnl_hook_ops mipv6_mn_tnl_rcv_stats_ops = { -+ {NULL, NULL}, -+ IP6_TNL_PRE_DECAP, -+ IP6_TNL_PRI_LAST, -+ mipv6_mn_tnl_rcv_stats_hook -+}; -+ -+static void mn_check_tunneled_packet(struct sk_buff *skb) -+{ -+ DEBUG_FUNC(); -+ /* If tunnel flag was set */ -+ if (skb->security & MIPV6_RCV_TUNNEL) { -+ struct in6_addr coa; -+ __u32 lifetime; -+ __u8 user_flags = 0; -+ int ptr = (u8*)(skb->nh.ipv6h+1) - skb->data; -+ int len = skb->len - ptr; -+ __u8 nexthdr = skb->nh.ipv6h->nexthdr; -+ -+ if (len < 0) -+ return; -+ -+ ptr = ipv6_skip_exthdr(skb, ptr, &nexthdr, len); -+ if (ptr < 0) -+ return; -+ -+ if (!mip6node_cnf.accept_ret_rout) { -+ DEBUG(DBG_INFO, "Return routability administratively disabled"); -+ return; -+ } -+ if (nexthdr == IPPROTO_MOBILITY) -+ return; -+ -+ /* Don't start RR when receiving ICMP error messages */ -+ if (nexthdr == IPPROTO_ICMPV6) { -+ u8 type; -+ -+ if (skb_copy_bits(skb, -+ ptr+offsetof(struct icmp6hdr, -+ icmp6_type), -+ &type, 1) -+ || !(type & ICMPV6_INFOMSG_MASK)) { -+ return; -+ } -+ } -+ user_flags |= mip6node_cnf.bu_cn_ack ? MIPV6_BU_F_ACK : 0; -+ mipv6_get_care_of_address(&skb->nh.ipv6h->daddr, &coa); -+ lifetime = mipv6_mn_get_bulifetime(&skb->nh.ipv6h->daddr, -+ &coa, 0); -+ -+ DEBUG(DBG_WARNING, "packet to address %x:%x:%x:%x:%x:%x:%x:%x" -+ "was tunneled. Sending BU to CN" -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&skb->nh.ipv6h->daddr), -+ NIPV6ADDR(&skb->nh.ipv6h->saddr)); -+ /* This should work also with home address option */ -+ -+ write_lock(&bul_lock); -+ mipv6_send_bu(&skb->nh.ipv6h->daddr, &skb->nh.ipv6h->saddr, -+ &coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, user_flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ } -+} -+ -+static int sched_mv_home_addr_task(struct in6_addr *haddr, int plen_new, -+ int newif, int oldif, struct handoff *ho) -+{ -+ int alloc_size; -+ struct ifr_holder *ifrh; -+ -+ alloc_size = sizeof(*ifrh) + (ho ? sizeof(*ho): 0); -+ if ((ifrh = kmalloc(alloc_size, GFP_ATOMIC)) == NULL) { -+ DEBUG(DBG_ERROR, "Out of memory"); -+ return -1; -+ } -+ if (ho) { -+ ifrh->ho = (struct handoff *)((struct ifr_holder *)(ifrh + 1)); -+ memcpy(ifrh->ho, ho, sizeof(*ho)); -+ } else -+ ifrh->ho = NULL; -+ -+ /* must queue task to avoid deadlock with rtnl */ -+ ifrh->ifr.ifr6_ifindex = newif; -+ ifrh->ifr.ifr6_prefixlen = plen_new; -+ ipv6_addr_copy(&ifrh->ifr.ifr6_addr, haddr); -+ ifrh->old_ifi = oldif; -+ -+ spin_lock_bh(&ifrh_lock); -+ list_add_tail(&ifrh->list, &ifrh_list); -+ spin_unlock_bh(&ifrh_lock); -+ -+ schedule_task(&mv_home_addr_task); -+ -+ return 0; -+} -+ -+static void send_ret_home_ns(struct in6_addr *ha_addr, -+ struct in6_addr *home_addr, -+ int ifindex) -+{ -+ struct in6_addr nil; -+ struct in6_addr mcaddr; -+ struct net_device *dev = dev_get_by_index(ifindex); -+ if (!dev) -+ return; -+ memset(&nil, 0, sizeof(nil)); -+ addrconf_addr_solict_mult(home_addr, &mcaddr); -+ ndisc_send_ns(dev, NULL, home_addr, &mcaddr, &nil); -+ dev_put(dev); -+} -+ -+static inline int ha_is_reachable(int ifindex, struct in6_addr *ha) -+{ -+ struct net_device *dev; -+ int reachable = 0; -+ -+ dev = dev_get_by_index(ifindex); -+ if (dev) { -+ struct neighbour *neigh; -+ if ((neigh = ndisc_get_neigh(dev, ha)) != NULL) { -+ read_lock_bh(&neigh->lock); -+ if (neigh->nud_state&NUD_VALID) -+ reachable = 1; -+ read_unlock_bh(&neigh->lock); -+ neigh_release(neigh); -+ } -+ dev_put(dev); -+ } -+ return reachable; -+} -+ -+static int mn_ha_handoff(struct handoff *ho) -+{ -+ struct list_head *lh; -+ struct mn_info *minfo; -+ struct in6_addr *coa= ho->coa; -+ int wait_mv_home = 0; -+ -+ read_lock_bh(&mn_info_lock); -+ list_for_each(lh, &mn_info_list) { -+ __u8 has_home_reg; -+ int ifindex; -+ struct in6_addr ha; -+ __u8 athome; -+ __u32 lifetime; -+ struct mipv6_bul_entry *entry = NULL; -+ -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ has_home_reg = minfo->has_home_reg; -+ ifindex = minfo->ifindex; -+ ipv6_addr_copy(&ha, &minfo->ha); -+ -+ if (mipv6_prefix_compare(&ho->rtr_addr, &minfo->home_addr, -+ ho->plen)) { -+ if (minfo->has_home_reg) -+ athome = minfo->is_at_home = MN_RETURNING_HOME; -+ else -+ athome = minfo->is_at_home = MN_AT_HOME; -+ coa = &minfo->home_addr; -+ -+ spin_unlock(&minfo->lock); -+#if 0 -+ /* Cancel prefix solicitation, rtr is our HA */ -+ mipv6_pfx_cancel_send(&ho->rtr_addr, ifindex); -+#endif -+ minfo->ifindex = ho->ifindex; -+ -+ if (minfo->has_home_reg && -+ !ha_is_reachable(ho->ifindex, &minfo->ha)) { -+ send_ret_home_ns(&minfo->ha, -+ &minfo->home_addr, -+ ho->ifindex); -+ mipv6_mdet_set_curr_rtr_reachable(0); -+ wait_mv_home++; -+ } -+ if (ifindex != ho->ifindex){ -+ wait_mv_home++; -+ DEBUG(DBG_INFO, -+ "Moving home address back to " -+ "the home interface"); -+ sched_mv_home_addr_task(&minfo->home_addr, -+ 128, -+ ho->ifindex, -+ ifindex, ho); -+ } -+ if (!has_home_reg || wait_mv_home) -+ continue; -+ -+ lifetime = 0; -+ -+ } else { -+ athome = minfo->is_at_home = MN_NOT_AT_HOME; -+ if (minfo->ifindex_user != minfo->ifindex) { -+ DEBUG(DBG_INFO, "Scheduling home address move to virtual interface"); -+ sched_mv_home_addr_task(&minfo->home_addr, -+ 128, -+ minfo->ifindex_user, -+ minfo->ifindex, ho); /* Is minfo->ifindex correct */ -+ -+ wait_mv_home++; -+ } -+ minfo->ifindex = minfo->ifindex_user; -+ spin_unlock(&minfo->lock); -+ if (wait_mv_home) -+ continue; -+ if (!has_home_reg && -+ init_home_registration(&minfo->home_addr, -+ ho->coa)) { -+ continue; -+ } -+ lifetime = mipv6_mn_get_bulifetime(&minfo->home_addr, -+ ho->coa, -+ MIPV6_BU_F_HOME); -+ -+ } -+ write_lock(&bul_lock); -+ if (!(entry = mipv6_bul_get(&ha, &minfo->home_addr)) || -+ !(entry->flags & MIPV6_BU_F_HOME)) { -+ DEBUG(DBG_ERROR, -+ "Unable to find home registration for " -+ "home address: %x:%x:%x:%x:%x:%x:%x:%x!\n", -+ NIPV6ADDR(&minfo->home_addr)); -+ write_unlock(&bul_lock); -+ continue; -+ } -+ DEBUG(DBG_INFO, "Sending home de ? %d registration for " -+ "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" -+ "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " -+ "with lifetime %ld", -+ (athome != MN_NOT_AT_HOME), -+ NIPV6ADDR(&entry->home_addr), -+ NIPV6ADDR(&entry->cn_addr), lifetime); -+ mipv6_send_bu(&entry->home_addr, &entry->cn_addr, -+ coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, entry->flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ -+ } -+ read_unlock_bh(&mn_info_lock); -+ return wait_mv_home; -+} -+/** -+ * mn_cn_handoff - called for every bul entry to send BU to CN -+ * @rawentry: bul entry -+ * @args: handoff event -+ * @sortkey: -+ * -+ * Since MN can have many home addresses and home networks, every BUL -+ * entry needs to be checked -+ **/ -+int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey) -+{ -+ struct mipv6_bul_entry *entry = (struct mipv6_bul_entry *)rawentry; -+ struct in6_addr *coa = (struct in6_addr *)args; -+ -+ DEBUG_FUNC(); -+ -+ /* Home registrations already handled by mn_ha_handoff */ -+ if (entry->flags & MIPV6_BU_F_HOME) -+ return ITERATOR_CONT; -+ -+ /* BUL is locked by mipv6_mobile_node_moved which calls us -+ through mipv6_bul_iterate */ -+ -+ if (mipv6_prefix_compare(coa, -+ &entry->home_addr, -+ 64)) { -+ mipv6_send_bu(&entry->home_addr, &entry->cn_addr, -+ &entry->home_addr, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, entry->flags, 0, -+ NULL); -+ } else { -+ u32 lifetime = mipv6_mn_get_bulifetime(&entry->home_addr, -+ coa, -+ entry->flags); -+ mipv6_send_bu(&entry->home_addr, &entry->cn_addr, -+ coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, entry->flags, -+ lifetime, NULL); -+ } -+ return ITERATOR_CONT; -+} -+ -+ -+int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey) -+{ -+ struct mipv6_bul_entry *bul = (struct mipv6_bul_entry *)rawentry; -+ struct bul_inval_args *arg = (struct bul_inval_args *)args; -+ -+ DEBUG_FUNC(); -+ -+ if (!ipv6_addr_cmp(arg->cn, &bul->cn_addr) && -+ (!ipv6_addr_cmp(arg->mn, &bul->home_addr) || -+ !ipv6_addr_cmp(arg->mn, &bul->coa))) { -+ if (arg->all_rr_states || !bul->rr || -+ (bul->rr->rr_state != RR_INIT && -+ bul->rr->rr_state != RR_DONE)) { -+ bul->state = ACK_ERROR; -+ bul->callback = bul_entry_expired; -+ bul->callback_time = jiffies + -+ DUMB_CN_BU_LIFETIME * HZ; -+ bul->expire = bul->callback_time; -+ DEBUG(DBG_INFO, "BUL entry set to ACK_ERROR"); -+ mipv6_bul_reschedule(bul); -+ } -+ } -+ return ITERATOR_CONT; -+} -+/** -+ * init_home_registration - start Home Registration process -+ * @home_addr: home address -+ * @coa: care-of address -+ * -+ * Checks whether we have a Home Agent address for this home address. -+ * If not starts Dynamic Home Agent Address Discovery. Otherwise -+ * tries to register with home agent if not already registered. -+ * Returns 1, if home registration process is started and 0 otherwise -+ **/ -+int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa) -+{ -+ struct mn_info *hinfo; -+ struct in6_addr ha; -+ __u8 man_conf; -+ int ifindex; -+ __u32 lifetime; -+ __u8 user_flags = 0, flags; -+ -+ DEBUG_FUNC(); -+ -+ read_lock_bh(&mn_info_lock); -+ if ((hinfo = mipv6_mninfo_get_by_home(home_addr)) == NULL) { -+ DEBUG(DBG_ERROR, "No mn_info found for address: " -+ "%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(home_addr)); -+ read_unlock_bh(&mn_info_lock); -+ return -ENOENT; -+ } -+ spin_lock(&hinfo->lock); -+ if (mipv6_prefix_compare(&hinfo->home_addr, coa, hinfo->home_plen)) { -+ spin_unlock(&hinfo->lock); -+ read_unlock_bh(&mn_info_lock); -+ DEBUG(DBG_INFO, "Adding home address, MN at home"); -+ return 1; -+ } -+ if (ipv6_addr_any(&hinfo->ha)) { -+ int dhaad_id = mipv6_get_dhaad_id(); -+ hinfo->dhaad_id = dhaad_id; -+ spin_unlock(&hinfo->lock); -+ mipv6_icmpv6_send_dhaad_req(home_addr, hinfo->home_plen, dhaad_id); -+ read_unlock_bh(&mn_info_lock); -+ DEBUG(DBG_INFO, -+ "Home Agent address not set, initiating DHAAD"); -+ return 1; -+ } -+ ipv6_addr_copy(&ha, &hinfo->ha); -+ man_conf = hinfo->man_conf; -+ ifindex = hinfo->ifindex; -+ spin_unlock(&hinfo->lock); -+ read_unlock_bh(&mn_info_lock); -+#if 0 -+ if (man_conf) -+ mipv6_pfx_add_ha(&ha, coa, ifindex); -+#endif -+ if (mipv6_bul_exists(&ha, home_addr)) { -+ DEBUG(DBG_INFO, "BU already sent to HA"); -+ return 0; -+ } -+ /* user flags received through sysctl */ -+ user_flags |= mip6node_cnf.bu_lladdr ? MIPV6_BU_F_LLADDR : 0; -+ user_flags |= mip6node_cnf.bu_keymgm ? MIPV6_BU_F_KEYMGM : 0; -+ -+ flags = MIPV6_BU_F_HOME | MIPV6_BU_F_ACK | user_flags; -+ -+ lifetime = mipv6_mn_get_bulifetime(home_addr, coa, flags); -+ -+ DEBUG(DBG_INFO, "Sending initial home registration for " -+ "home address: %x:%x:%x:%x:%x:%x:%x:%x\n" -+ "to home agent %x:%x:%x:%x:%x:%x:%x:%x, " -+ "with lifetime %ld, prefixlength %d", -+ NIPV6ADDR(home_addr), NIPV6ADDR(&ha), lifetime, 0); -+ -+ write_lock_bh(&bul_lock); -+ mipv6_send_bu(home_addr, &ha, coa, INITIAL_BINDACK_DAD_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, flags, lifetime, NULL); -+ write_unlock_bh(&bul_lock); -+ -+ return 1; -+} -+ -+/** -+ * mipv6_mobile_node_moved - Send BUs to all HAs and CNs -+ * @ho: handoff structure contains the new and previous routers -+ * -+ * Event for handoff. Sends BUs everyone on Binding Update List. -+ **/ -+int mipv6_mobile_node_moved(struct handoff *ho) -+{ -+#if 0 -+ int bu_to_prev_router = 1; -+#endif -+ int dummy; -+ -+ DEBUG_FUNC(); -+ -+ ma_ctl_upd_iface(ho->ifindex, -+ MA_IFACE_CURRENT | MA_IFACE_HAS_ROUTER, &dummy); -+ -+ /* First send BU to HA, then to all other nodes that are on BU list */ -+ if (mn_ha_handoff(ho) != 0) -+ return 0; /* Wait for move home address task */ -+#if 0 -+ /* Add current care-of address to mn_info list, if current router acts -+ as a HA.*/ -+ -+ if (ho->home_address && bu_to_prev_router) -+ mipv6_mninfo_add(ho->coa, ho->plen, -+ MN_AT_HOME, 0, &ho->rtr_addr, -+ ho->plen, ROUTER_BU_DEF_LIFETIME, -+ 0); -+ -+#endif -+ return 0; -+} -+ -+/** -+ * mipv6_mn_send_home_na - send NA when returning home -+ * @haddr: home address to advertise -+ * -+ * After returning home, MN must advertise all its valid addresses in -+ * home link to all nodes. -+ **/ -+void mipv6_mn_send_home_na(struct in6_addr *haddr) -+{ -+ struct net_device *dev = NULL; -+ struct in6_addr mc_allnodes; -+ struct mn_info *hinfo = NULL; -+ -+ read_lock(&mn_info_lock); -+ hinfo = mipv6_mninfo_get_by_home(haddr); -+ if (!hinfo) { -+ read_unlock(&mn_info_lock); -+ return; -+ } -+ spin_lock(&hinfo->lock); -+ hinfo->is_at_home = MN_AT_HOME; -+ dev = dev_get_by_index(hinfo->ifindex); -+ spin_unlock(&hinfo->lock); -+ read_unlock(&mn_info_lock); -+ if (dev == NULL) { -+ DEBUG(DBG_ERROR, "Send home_na: device not found."); -+ return; -+ } -+ -+ ipv6_addr_all_nodes(&mc_allnodes); -+ ndisc_send_na(dev, NULL, &mc_allnodes, haddr, 0, 0, 1, 1); -+ dev_put(dev); -+} -+ -+static int mn_use_hao(struct in6_addr *daddr, struct in6_addr *saddr) -+{ -+ struct mipv6_bul_entry *entry; -+ struct mn_info *minfo = NULL; -+ int add_ha = 0; -+ -+ read_lock_bh(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_home(saddr); -+ if (minfo && minfo->is_at_home != MN_AT_HOME) { -+ read_lock_bh(&bul_lock); -+ if ((entry = mipv6_bul_get(daddr, saddr)) == NULL) { -+ read_unlock_bh(&bul_lock); -+ read_unlock_bh(&mn_info_lock); -+ return add_ha; -+ } -+ add_ha = (entry->state != ACK_ERROR && -+ (!entry->rr || entry->rr->rr_state == RR_DONE || -+ entry->flags & MIPV6_BU_F_HOME)); -+ read_unlock_bh(&bul_lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+ return add_ha; -+} -+ -+static int -+mn_dev_event(struct notifier_block *nb, unsigned long event, void *ptr) -+{ -+ struct net_device *dev = ptr; -+ struct list_head *lh; -+ struct mn_info *minfo; -+ int newif = 0; -+ -+ /* here are probably the events we need to worry about */ -+ switch (event) { -+ case NETDEV_UP: -+ DEBUG(DBG_DATADUMP, "New netdevice %s registered.", dev->name); -+ if (dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) -+ ma_ctl_add_iface(dev->ifindex); -+ -+ break; -+ case NETDEV_GOING_DOWN: -+ DEBUG(DBG_DATADUMP, "Netdevice %s disappeared.", dev->name); -+ /* -+ * Go through mn_info list and move all home addresses on the -+ * netdev going down to a new device. This will make it -+ * practically impossible for the home address to return home, -+ * but allow MN to retain its connections using the address. -+ */ -+ -+ read_lock_bh(&mn_info_lock); -+ list_for_each(lh, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ if (minfo->ifindex == dev->ifindex) { -+ if (sched_mv_home_addr_task(&minfo->home_addr, 128, -+ minfo->ifindex_user, -+ 0, NULL) < 0) { -+ minfo->ifindex = 0; -+ spin_unlock(&minfo->lock); -+ read_unlock_bh(&mn_info_lock); -+ return NOTIFY_DONE; -+ } else { -+ minfo->ifindex = minfo->ifindex_user; -+ if (minfo->is_at_home) { -+ minfo->is_at_home = 0; -+ -+ } -+ newif = minfo->ifindex_user; -+ } -+ } -+ spin_unlock(&minfo->lock); -+ } -+ -+ read_unlock_bh(&mn_info_lock); -+ } -+ ma_ctl_upd_iface(dev->ifindex, MA_IFACE_NOT_PRESENT, &newif); -+ mipv6_mdet_del_if(dev->ifindex); -+ -+ return NOTIFY_DONE; -+} -+ -+struct notifier_block mipv6_mn_dev_notifier = { -+ mn_dev_event, -+ NULL, -+ 0 /* check if using zero is ok */ -+}; -+ -+static void deprecate_addr(struct mn_info *minfo) -+{ -+ /* -+ * Lookup address from IPv6 address list and set deprecated flag -+ */ -+ -+} -+ -+/* -+ * Required because we can only modify addresses after the packet is -+ * constructed. We otherwise mess with higher level protocol -+ * pseudoheaders. With strict protocol layering life would be SO much -+ * easier! -+ */ -+static unsigned int modify_xmit_addrs(unsigned int hooknum, -+ struct sk_buff **pskb, -+ const struct net_device *in, -+ const struct net_device *out, -+ int (*okfn) (struct sk_buff *)) -+{ -+ struct sk_buff *skb = *pskb; -+ -+ DEBUG_FUNC(); -+ -+ if (skb) { -+ struct ipv6hdr *hdr = skb->nh.ipv6h; -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; -+ struct mipv6_bul_entry *bule; -+ struct in6_addr *daddr; -+ -+ if (!ipv6_addr_any(&opt->hoa)) -+ daddr = &opt->hoa; -+ else -+ daddr = &hdr->daddr; -+ -+ /* We don't consult bul when sending a BU to avoid deadlock, since -+ * BUL is already locked. -+ */ -+ -+ -+ if (opt->mipv6_flags & MIPV6_SND_HAO && -+ !(opt->mipv6_flags & MIPV6_SND_BU)) { -+ write_lock(&bul_lock); -+ bule = mipv6_bul_get(daddr, &hdr->saddr); -+ if (!bule) { -+ write_unlock(&bul_lock); -+ return NF_ACCEPT; -+ } -+ if (!bule->rr || bule->rr->rr_state == RR_DONE || -+ bule->flags & MIPV6_BU_F_HOME) { -+ DEBUG(DBG_DATADUMP, -+ "Replace source address with CoA and reroute"); -+ ipv6_addr_copy(&hdr->saddr, &bule->coa); -+ skb->nfcache |= NFC_ALTERED; -+ } -+ write_unlock(&bul_lock); -+ } else if (opt->mipv6_flags & MIPV6_SND_HAO) { -+ mipv6_get_care_of_address(&hdr->saddr, &hdr->saddr); -+ skb->nfcache |= NFC_ALTERED; -+ } -+ } -+ return NF_ACCEPT; -+} -+ -+/* We set a netfilter hook so that we can modify outgoing packet's -+ * source addresses -+ */ -+struct nf_hook_ops addr_modify_hook_ops = { -+ {NULL, NULL}, /* List head, no predecessor, no successor */ -+ modify_xmit_addrs, -+ PF_INET6, -+ NF_IP6_LOCAL_OUT, -+ NF_IP6_PRI_FIRST /* Should be of EXTREMELY high priority since we -+ * do not want to mess with IPSec (possibly -+ * implemented as packet filter) -+ */ -+}; -+ -+#define MN_INFO_LEN 77 -+ -+static int mn_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct list_head *p; -+ struct mn_info *minfo; -+ int len = 0, skip = 0; -+ -+ DEBUG_FUNC(); -+ -+ read_lock_bh(&mn_info_lock); -+ list_for_each(p, &mn_info_list) { -+ if (len < offset / MN_INFO_LEN) { -+ skip++; -+ continue; -+ } -+ if (len >= length) -+ break; -+ minfo = list_entry(p, struct mn_info, list); -+ spin_lock(&minfo->lock); -+ len += sprintf(buffer + len, "%02d %08x%08x%08x%08x %02x " -+ "%08x%08x%08x%08x %d %d\n", -+ minfo->ifindex, -+ ntohl(minfo->home_addr.s6_addr32[0]), -+ ntohl(minfo->home_addr.s6_addr32[1]), -+ ntohl(minfo->home_addr.s6_addr32[2]), -+ ntohl(minfo->home_addr.s6_addr32[3]), -+ minfo->home_plen, -+ ntohl(minfo->ha.s6_addr32[0]), -+ ntohl(minfo->ha.s6_addr32[1]), -+ ntohl(minfo->ha.s6_addr32[2]), -+ ntohl(minfo->ha.s6_addr32[3]), -+ minfo->is_at_home, minfo->has_home_reg); -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock_bh(&mn_info_lock); -+ -+ *start = buffer; -+ if (offset) -+ *start += offset % MN_INFO_LEN; -+ -+ len -= offset % MN_INFO_LEN; -+ -+ if (len > length) -+ len = length; -+ if (len < 0) -+ len = 0; -+ -+ return len; -+} -+ -+int mipv6_mn_ha_nd_update(struct net_device *dev, -+ struct in6_addr *ha, u8 *lladdr) -+{ -+ int valid = 0; -+ struct neighbour *neigh; -+ if ((neigh = ndisc_get_neigh(dev, ha))) { -+ read_lock(&neigh->lock); -+ valid = neigh->nud_state & NUD_VALID; -+ read_unlock(&neigh->lock); -+ if (!valid && lladdr) -+ neigh_update(neigh, lladdr, NUD_REACHABLE, 0, 1); -+ neigh_release(neigh); -+ } -+ return valid; -+} -+ -+int mipv6_mn_ha_probe(struct inet6_ifaddr *ifp, u8 *lladdr) -+{ -+ struct mn_info *minfo; -+ -+ if (!(minfo = mipv6_mninfo_get_by_home(&ifp->addr)) || -+ ipv6_addr_any(&minfo->ha)) -+ return 0; -+ -+ if (mipv6_mn_ha_nd_update(ifp->idev->dev, &minfo->ha, lladdr)) -+ mipv6_mdet_retrigger_ho(); -+ return 1; -+} -+ -+int __init mipv6_mn_init(void) -+{ -+ struct net_device *dev; -+ -+ DEBUG_FUNC(); -+ -+ if (mipv6_add_tnl_to_ha()) -+ return -ENODEV; -+ -+ mipv6_bul_init(MIPV6_BUL_SIZE); -+ mip6_fn.mn_use_hao = mn_use_hao; -+ mip6_fn.mn_check_tunneled_packet = mn_check_tunneled_packet; -+ INIT_TQUEUE(&mv_home_addr_task, mv_home_addr, NULL); -+ -+ ma_ctl_init(); -+ for (dev = dev_base; dev; dev = dev->next) { -+ if (dev->flags & IFF_UP && -+ dev->type != ARPHRD_LOOPBACK && !dev_is_mip6_tnl(dev)) { -+ ma_ctl_add_iface(dev->ifindex); -+ } -+ } -+ DEBUG(DBG_INFO, "Multiaccess support initialized"); -+ -+ register_netdevice_notifier(&mipv6_mn_dev_notifier); -+ register_inet6addr_notifier(&mipv6_mn_inet6addr_notifier); -+ -+ ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_send_bu_ops); -+ ip6ip6_tnl_register_hook(&mipv6_mn_tnl_xmit_stats_ops); -+ ip6ip6_tnl_register_hook(&mipv6_mn_tnl_rcv_stats_ops); -+ -+ MIPV6_SETCALL(mipv6_set_home, mipv6_mn_set_home); -+ -+ mipv6_initialize_mdetect(); -+ -+ /* COA to home transformation hook */ -+ MIPV6_SETCALL(mipv6_get_home_address, mipv6_get_saddr_hook); -+ MIPV6_SETCALL(mipv6_mn_ha_probe, mipv6_mn_ha_probe); -+ MIPV6_SETCALL(mipv6_is_home_addr, mipv6_mn_is_home_addr); -+ proc_net_create("mip6_mninfo", 0, mn_proc_info); -+ /* Set packet modification hook (source addresses) */ -+ nf_register_hook(&addr_modify_hook_ops); -+ -+ return 0; -+} -+ -+void __exit mipv6_mn_exit(void) -+{ -+ struct list_head *lh, *tmp; -+ struct mn_info *minfo; -+ DEBUG_FUNC(); -+ -+ mip6_fn.mn_use_hao = NULL; -+ mip6_fn.mn_check_tunneled_packet = NULL; -+ -+ MIPV6_RESETCALL(mipv6_set_home); -+ MIPV6_RESETCALL(mipv6_get_home_address); -+ MIPV6_RESETCALL(mipv6_mn_ha_probe); -+ MIPV6_RESETCALL(mipv6_is_home_addr); -+ nf_unregister_hook(&addr_modify_hook_ops); -+ proc_net_remove("mip6_mninfo"); -+ mipv6_shutdown_mdetect(); -+ ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_stats_ops); -+ ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_xmit_stats_ops); -+ ip6ip6_tnl_unregister_hook(&mipv6_mn_tnl_rcv_send_bu_ops); -+ ma_ctl_clean(); -+ -+ unregister_inet6addr_notifier(&mipv6_mn_inet6addr_notifier); -+ unregister_netdevice_notifier(&mipv6_mn_dev_notifier); -+ write_lock_bh(&mn_info_lock); -+ -+ list_for_each_safe(lh, tmp, &mn_info_list) { -+ minfo = list_entry(lh, struct mn_info, list); -+ if (minfo->is_at_home == MN_NOT_AT_HOME) -+ deprecate_addr(minfo); -+ list_del(&minfo->list); -+ kfree(minfo); -+ } -+ write_unlock_bh(&mn_info_lock); -+ mipv6_bul_exit(); -+ flush_scheduled_tasks(); -+ mipv6_del_tnl_to_ha(); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mn.h -@@ -0,0 +1,96 @@ -+/* -+ * MIPL Mobile IPv6 Mobile Node header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MN_H -+#define _MN_H -+ -+#include <linux/in6.h> -+ -+/* constants for sending of BUs*/ -+#define HA_BU_DEF_LIFETIME 10000 -+#define CN_BU_DEF_LIFETIME 420 /* Max lifetime for RR bindings from RFC 3775 */ -+#define DUMB_CN_BU_LIFETIME 600 /* BUL entry lifetime in case of dumb CN */ -+#define ROUTER_BU_DEF_LIFETIME 30 /* For packet forwarding from previous coa */ -+#define ERROR_DEF_LIFETIME DUMB_CN_BU_LIFETIME -+ -+extern rwlock_t mn_info_lock; -+ -+#define MN_NOT_AT_HOME 0 -+#define MN_RETURNING_HOME 1 -+#define MN_AT_HOME 2 -+ -+/* -+ * Mobile Node information record -+ */ -+struct mn_info { -+ struct in6_addr home_addr; -+ struct in6_addr ha; -+ __u8 home_plen; -+ __u8 is_at_home; -+ __u8 has_home_reg; -+ __u8 man_conf; -+ int ifindex; -+ int ifindex_user; -+ unsigned long home_addr_expires; -+ unsigned short dhaad_id; -+ struct list_head list; -+ spinlock_t lock; -+}; -+ -+/* prototypes for interface functions */ -+int mipv6_mn_init(void); -+void mipv6_mn_exit(void); -+ -+struct handoff; -+ -+/* Interface to movement detection */ -+int mipv6_mobile_node_moved(struct handoff *ho); -+ -+void mipv6_mn_send_home_na(struct in6_addr *haddr); -+/* Init home reg. with coa */ -+int init_home_registration(struct in6_addr *home_addr, struct in6_addr *coa); -+ -+/* mn_info functions that require locking by caller */ -+struct mn_info *mipv6_mninfo_get_by_home(struct in6_addr *haddr); -+ -+struct mn_info *mipv6_mninfo_get_by_ha(struct in6_addr *home_agent); -+ -+struct mn_info *mipv6_mninfo_get_by_id(unsigned short id); -+ -+/* "safe" mn_info functions */ -+void mipv6_mninfo_add(int ifindex, struct in6_addr *home_addr, int plen, -+ int isathome, unsigned long lifetime, struct in6_addr *ha, -+ int ha_plen, unsigned long ha_lifetime, int man_conf); -+ -+int mipv6_mninfo_del(struct in6_addr *home_addr, int del_dyn_only); -+ -+void mipv6_mn_set_home_reg(struct in6_addr *home_addr, int has_home_reg); -+ -+int mipv6_mn_is_at_home(struct in6_addr *addr); -+ -+int mipv6_mn_is_home_addr(struct in6_addr *addr); -+ -+__u32 mipv6_mn_get_bulifetime(struct in6_addr *home_addr, -+ struct in6_addr *coa, __u8 flags); -+int mn_cn_handoff(void *rawentry, void *args, unsigned long *sortkey); -+ -+int mipv6_mn_ha_nd_update(struct net_device *dev, -+ struct in6_addr *ha, u8 *lladdr); -+ -+struct bul_inval_args { -+ int all_rr_states; -+ struct in6_addr *cn; -+ struct in6_addr *mn; -+}; -+ -+int mn_bul_invalidate(void *rawentry, void *args, unsigned long *sortkey); -+ -+#endif /* _MN_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr.h -@@ -0,0 +1,101 @@ -+/* -+ * MIPL Mobile IPv6 Mobility Header send and receive -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _MOBHDR_H -+#define _MOBHDR_H -+ -+#include <net/mipv6.h> -+ -+/* RR states for mipv6_send_bu() */ -+#define RR_INIT 0x00 -+#define RR_WAITH 0x01 -+#define RR_WAITC 0x02 -+#define RR_WAITHC 0x13 -+#define RR_DONE 0x10 -+ -+#define MH_UNKNOWN_CN 1 -+#define MH_AUTH_FAILED 2 -+#define MH_SEQUENCE_MISMATCH 3 -+ -+struct mipv6_bul_entry; -+struct sk_buff; -+ -+int mipv6_mh_common_init(void); -+void mipv6_mh_common_exit(void); -+int mipv6_mh_mn_init(void); -+void mipv6_mh_mn_exit(void); -+ -+struct mipv6_mh_opt { -+ struct mipv6_mo_alt_coa *alt_coa; -+ struct mipv6_mo_nonce_indices *nonce_indices; -+ struct mipv6_mo_bauth_data *auth_data; -+ struct mipv6_mo_br_advice *br_advice; -+ int freelen; -+ int totlen; -+ u8 *next_free; -+ u8 data[0]; -+}; -+ -+struct mobopt { -+ struct mipv6_mo_alt_coa *alt_coa; -+ struct mipv6_mo_nonce_indices *nonce_indices; -+ struct mipv6_mo_bauth_data *auth_data; -+ struct mipv6_mo_br_advice *br_advice; -+}; -+ -+struct mipv6_mh_opt *alloc_mh_opts(int totlen); -+int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data); -+int parse_mo_tlv(void *mos, int len, struct mobopt *opts); -+int mipv6_add_pad(u8 *data, int n); -+ -+struct mipv6_auth_parm { -+ struct in6_addr *coa; -+ struct in6_addr *cn_addr; -+ __u8 *k_bu; -+}; -+ -+int send_mh(struct in6_addr *daddr, struct in6_addr *saddr, -+ u8 msg_type, u8 msg_len, u8 *msg, -+ struct in6_addr *hao_addr, struct in6_addr *rth_addr, -+ struct mipv6_mh_opt *ops, struct mipv6_auth_parm *parm); -+ -+int mipv6_mh_register(int type, int (*func)(struct sk_buff *, -+ struct in6_addr *, struct in6_addr *, -+ struct in6_addr *, struct in6_addr *, struct mipv6_mh *)); -+ -+void mipv6_mh_unregister(int type); -+ -+int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct mipv6_mh_opt *ops); -+ -+int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *coa, __u32 initdelay, -+ __u32 maxackdelay, __u8 exp, __u8 flags, -+ __u32 lifetime, struct mipv6_mh_opt *ops); -+ -+int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *home, __u8 status); -+ -+int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *auth_coa, struct in6_addr *rep_coa, -+ u8 status, u16 sequence, u32 lifetime, u8 *k_bu); -+ -+/* Binding Authentication Data Option routines */ -+#define MAX_HASH_LENGTH 20 -+#define MIPV6_RR_MAC_LENGTH 12 -+ -+int mipv6_auth_build(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *opt, __u8 *aud_data, __u8 *k_bu); -+ -+int mipv6_auth_check(struct in6_addr *cn_addr, struct in6_addr *coa, -+ __u8 *opt, __u8 optlen, struct mipv6_mo_bauth_data *aud, -+ __u8 *k_bu); -+#endif /* _MOBHDR_H */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr_common.c -@@ -0,0 +1,1210 @@ -+/* -+ * Mobile IPv6 Mobility Header Common Functions -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id: s.mh_recv.c 1.159 02/10/16 15:01:29+03:00 antti@traci.mipl.mediapoli.com $ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/types.h> -+#include <linux/in6.h> -+#include <linux/skbuff.h> -+#include <linux/ipsec.h> -+#include <linux/init.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+#include <net/checksum.h> -+#include <net/protocol.h> -+ -+#include "stats.h" -+#include "debug.h" -+#include "mobhdr.h" -+#include "bcache.h" -+ -+#include "rr_crypto.h" -+#include "exthdrs.h" -+#include "config.h" -+ -+#define MIPV6_MH_MAX MIPV6_MH_BE -+struct mh_proto { -+ int (*func) (struct sk_buff *, -+ struct in6_addr *, struct in6_addr *, -+ struct in6_addr *, struct in6_addr *, -+ struct mipv6_mh *); -+}; -+ -+static struct mh_proto mh_rcv[MIPV6_MH_MAX]; -+ -+int mipv6_mh_register(int type, int (*func)(struct sk_buff *, -+ struct in6_addr *, struct in6_addr *, -+ struct in6_addr *, struct in6_addr *, struct mipv6_mh *)) -+{ -+ if (mh_rcv[type].func != NULL) -+ return -1; -+ -+ mh_rcv[type].func = func; -+ -+ return 0; -+} -+ -+void mipv6_mh_unregister(int type) -+{ -+ if (type < 0 || type > MIPV6_MH_MAX) -+ return; -+ -+ mh_rcv[type].func = NULL; -+} -+ -+struct socket *mipv6_mh_socket = NULL; -+ -+/* TODO: Fix fragmentation */ -+static int dstopts_getfrag( -+ const void *data, struct in6_addr *addr, -+ char *buff, unsigned int offset, unsigned int len) -+{ -+ memcpy(buff, data + offset, len); -+ return 0; -+} -+ -+struct mipv6_mh_opt *alloc_mh_opts(int totlen) -+{ -+ struct mipv6_mh_opt *ops; -+ -+ ops = kmalloc(sizeof(*ops) + totlen, GFP_ATOMIC); -+ if (ops == NULL) -+ return NULL; -+ -+ memset(ops, 0, sizeof(*ops)); -+ ops->next_free = ops->data; -+ ops->freelen = totlen; -+ -+ return ops; -+} -+ -+int append_mh_opt(struct mipv6_mh_opt *ops, u8 type, u8 len, void *data) -+{ -+ struct mipv6_mo *mo; -+ -+ if (ops->next_free == NULL) { -+ DEBUG(DBG_ERROR, "No free room for option"); -+ return -ENOMEM; -+ } -+ if (ops->freelen < len + 2) { -+ DEBUG(DBG_ERROR, "No free room for option"); -+ return -ENOMEM; -+ } -+ else { -+ ops->freelen -= (len + 2); -+ ops->totlen += (len + 2); -+ } -+ -+ mo = (struct mipv6_mo *)ops->next_free; -+ mo->type = type; -+ mo->length = len; -+ -+ switch (type) { -+ case MIPV6_OPT_ALTERNATE_COA: -+ ops->alt_coa = (struct mipv6_mo_alt_coa *)mo; -+ ipv6_addr_copy(&ops->alt_coa->addr, (struct in6_addr *)data); -+ break; -+ case MIPV6_OPT_NONCE_INDICES: -+ DEBUG(DBG_INFO, "Added nonce indices pointer"); -+ ops->nonce_indices = (struct mipv6_mo_nonce_indices *)mo; -+ ops->nonce_indices->home_nonce_i = *(__u16 *)data; -+ ops->nonce_indices->careof_nonce_i = *((__u16 *)data + 1); -+ break; -+ case MIPV6_OPT_AUTH_DATA: -+ DEBUG(DBG_INFO, "Added opt auth_data pointer"); -+ ops->auth_data = (struct mipv6_mo_bauth_data *)mo; -+ break; -+ case MIPV6_OPT_BIND_REFRESH_ADVICE: -+ ops->br_advice = (struct mipv6_mo_br_advice *)mo; -+ ops->br_advice->refresh_interval = htons(*(u16 *)data); -+ break; -+ default: -+ DEBUG(DBG_ERROR, "Unknow option type"); -+ break; -+ } -+ -+ if (ops->freelen == 0) -+ ops->next_free = NULL; -+ else -+ ops->next_free += (len + 2); -+ -+ return 0; -+} -+ -+/* -+ * Calculates required padding with xn + y requirement with offset -+ */ -+static inline int optpad(int xn, int y, int offset) -+{ -+ return ((y - offset) & (xn - 1)); -+} -+ -+static int option_pad(int type, int offset) -+{ -+ if (type == MIPV6_OPT_ALTERNATE_COA) -+ return optpad(8, 6, offset); /* 8n + 6 */ -+ if (type == MIPV6_OPT_BIND_REFRESH_ADVICE || -+ type == MIPV6_OPT_NONCE_INDICES) -+ return optpad(2, 0, offset); /* 2n */ -+ return 0; -+} -+ -+/* -+ * Add Pad1 or PadN option to data -+ */ -+int mipv6_add_pad(u8 *data, int n) -+{ -+ struct mipv6_mo_padn *padn; -+ -+ if (n <= 0) return 0; -+ if (n == 1) { -+ *data = MIPV6_OPT_PAD1; -+ return 1; -+ } -+ padn = (struct mipv6_mo_padn *)data; -+ padn->type = MIPV6_OPT_PADN; -+ padn->length = n - 2; -+ memset(padn->data, 0, n - 2); -+ return n; -+} -+ -+/* -+ * Write options to mobility header buffer -+ */ -+static int prepare_mh_opts(u8 *optdata, int off, struct mipv6_mh_opt *ops) -+{ -+ u8 *nextopt = optdata; -+ int offset = off, pad = 0; -+ -+ if (ops == NULL) { -+ nextopt = NULL; -+ return -1; -+ } -+ -+ if (ops->alt_coa) { -+ pad = option_pad(MIPV6_OPT_ALTERNATE_COA, offset); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->alt_coa, sizeof(struct mipv6_mo_alt_coa)); -+ nextopt += sizeof(struct mipv6_mo_alt_coa); -+ offset += pad + sizeof(struct mipv6_mo_alt_coa); -+ } -+ -+ if (ops->br_advice) { -+ pad = option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->br_advice, sizeof(struct mipv6_mo_br_advice)); -+ nextopt += sizeof(struct mipv6_mo_br_advice); -+ offset += pad + sizeof(struct mipv6_mo_br_advice); -+ } -+ -+ if (ops->nonce_indices) { -+ pad = option_pad(MIPV6_OPT_NONCE_INDICES, offset); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->nonce_indices, sizeof(struct mipv6_mo_nonce_indices)); -+ nextopt += sizeof(struct mipv6_mo_nonce_indices); -+ offset += pad + sizeof(struct mipv6_mo_nonce_indices); -+ } -+ -+ if (ops->auth_data) { -+ /* This option should always be the last. Header -+ * length must be a multiple of 8 octects, so we pad -+ * if necessary. */ -+ pad = optpad(8, 0, offset + ops->auth_data->length + 2); -+ nextopt += mipv6_add_pad(nextopt, pad); -+ memcpy(nextopt, ops->auth_data, ops->auth_data->length + 2); -+ nextopt += ops->auth_data->length + 2; -+ } -+ nextopt = NULL; -+ -+ return 0; -+} -+ -+static int calculate_mh_opts(struct mipv6_mh_opt *ops, int mh_len) -+{ -+ int offset = mh_len; -+ -+ if (ops == NULL) -+ return 0; -+ -+ if (ops->alt_coa) -+ offset += sizeof(struct mipv6_mo_alt_coa) -+ + option_pad(MIPV6_OPT_ALTERNATE_COA, offset); -+ -+ if (ops->br_advice) -+ offset += sizeof(struct mipv6_mo_br_advice) -+ + option_pad(MIPV6_OPT_BIND_REFRESH_ADVICE, offset); -+ -+ if (ops->nonce_indices) -+ offset += sizeof(struct mipv6_mo_nonce_indices) -+ + option_pad(MIPV6_OPT_NONCE_INDICES, offset); -+ -+ if (ops->auth_data) /* no alignment */ -+ offset += ops->auth_data->length + 2; -+ -+ return offset - mh_len; -+} -+ -+/* -+ * -+ * Mobility Header Message send functions -+ * -+ */ -+ -+/** -+ * send_mh - builds and sends a MH msg -+ * -+ * @daddr: destination address for packet -+ * @saddr: source address for packet -+ * @msg_type: type of MH -+ * @msg_len: message length -+ * @msg: MH type specific data -+ * @hao_addr: home address for home address option -+ * @rth_addr: routing header address -+ * @ops: mobility options -+ * @parm: auth data -+ * -+ * Builds MH, appends the type specific msg data to the header and -+ * sends the packet with a home address option, if a home address was -+ * given. Returns 0, if everything succeeded and a negative error code -+ * otherwise. -+ **/ -+int send_mh(struct in6_addr *daddr, -+ struct in6_addr *saddr, -+ u8 msg_type, u8 msg_len, u8 *msg, -+ struct in6_addr *hao_addr, -+ struct in6_addr *rth_addr, -+ struct mipv6_mh_opt *ops, -+ struct mipv6_auth_parm *parm) -+{ -+ struct flowi fl; -+ struct mipv6_mh *mh; -+ struct sock *sk = mipv6_mh_socket->sk; -+ struct ipv6_txoptions *txopt = NULL; -+ int tot_len = sizeof(struct mipv6_mh) + msg_len; -+ int padded_len = 0, txopt_len = 0; -+ -+ DEBUG_FUNC(); -+ /* Add length of options */ -+ tot_len += calculate_mh_opts(ops, tot_len); -+ /* Needs to be a multiple of 8 octets */ -+ padded_len = tot_len + optpad(8, 0, tot_len); -+ -+ mh = sock_kmalloc(sk, padded_len, GFP_ATOMIC); -+ if (!mh) { -+ DEBUG(DBG_ERROR, "memory allocation failed"); -+ return -ENOMEM; -+ } -+ -+ memset(&fl, 0, sizeof(fl)); -+ fl.proto = IPPROTO_MOBILITY; -+ fl.fl6_dst = daddr; -+ fl.fl6_src = saddr; -+ fl.fl6_flowlabel = 0; -+ fl.oif = sk->bound_dev_if; -+ -+ if (hao_addr || rth_addr) { -+ __u8 *opt_ptr; -+ -+ if (hao_addr) -+ txopt_len += sizeof(struct mipv6_dstopt_homeaddr) + 6; -+ if (rth_addr) -+ txopt_len += sizeof(struct rt2_hdr); -+ -+ txopt_len += sizeof(*txopt); -+ txopt = sock_kmalloc(sk, txopt_len, GFP_ATOMIC); -+ if (txopt == NULL) { -+ DEBUG(DBG_ERROR, "No socket space left"); -+ sock_kfree_s(sk, mh, padded_len); -+ return -ENOMEM; -+ } -+ memset(txopt, 0, txopt_len); -+ txopt->tot_len = txopt_len; -+ opt_ptr = (__u8 *) (txopt + 1); -+ if (hao_addr) { -+ int holen = sizeof(struct mipv6_dstopt_homeaddr) + 6; -+ txopt->dst1opt = (struct ipv6_opt_hdr *) opt_ptr; -+ txopt->opt_flen += holen; -+ opt_ptr += holen; -+ mipv6_append_dst1opts(txopt->dst1opt, saddr, -+ NULL, holen); -+ txopt->mipv6_flags = MIPV6_SND_HAO | MIPV6_SND_BU; -+ } -+ if (rth_addr) { -+ int rtlen = sizeof(struct rt2_hdr); -+ txopt->srcrt2 = (struct ipv6_rt_hdr *) opt_ptr; -+ txopt->opt_nflen += rtlen; -+ opt_ptr += rtlen; -+ mipv6_append_rt2hdr(txopt->srcrt2, rth_addr); -+ } -+ } -+ -+ /* Fill in the fields of MH */ -+ mh->payload = NEXTHDR_NONE; -+ mh->length = (padded_len >> 3) - 1; /* Units of 8 octets - 1 */ -+ mh->type = msg_type; -+ mh->reserved = 0; -+ mh->checksum = 0; -+ -+ memcpy(mh->data, msg, msg_len); -+ prepare_mh_opts(mh->data + msg_len, msg_len + sizeof(*mh), ops); -+ /* If BAD is present, this is already done. */ -+ mipv6_add_pad((u8 *)mh + tot_len, padded_len - tot_len); -+ -+ if (parm && parm->k_bu && ops && ops->auth_data) { -+ /* Calculate the position of the authorization data before adding checksum*/ -+ mipv6_auth_build(parm->cn_addr, parm->coa, (__u8 *)mh, -+ (__u8 *)mh + padded_len - MIPV6_RR_MAC_LENGTH, parm->k_bu); -+ } -+ /* Calculate the MH checksum */ -+ mh->checksum = csum_ipv6_magic(fl.fl6_src, fl.fl6_dst, -+ padded_len, IPPROTO_MOBILITY, -+ csum_partial((char *)mh, padded_len, 0)); -+ ip6_build_xmit(sk, dstopts_getfrag, mh, &fl, padded_len, txopt, 255, -+ MSG_DONTWAIT); -+ /* dst cache must be cleared so RR messages can be routed through -+ different interfaces */ -+ sk_dst_reset(sk); -+ -+ if (txopt_len) -+ sock_kfree_s(sk, txopt, txopt_len); -+ sock_kfree_s(sk, mh, padded_len); -+ return 0; -+} -+ -+/** -+ * mipv6_send_brr - send a Binding Refresh Request -+ * @saddr: source address for BRR -+ * @daddr: destination address for BRR -+ * @ops: mobility options -+ * -+ * Sends a binding request. On a mobile node, use the mobile node's -+ * home address for @saddr. Returns 0 on success, negative on -+ * failure. -+ **/ -+int mipv6_send_brr(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct mipv6_mh_opt *ops) -+{ -+ struct mipv6_mh_brr br; -+ -+ memset(&br, 0, sizeof(br)); -+ /* We don't need to explicitly add a RH to brr, since it will be -+ * included automatically, if a BCE exists -+ */ -+ MIPV6_INC_STATS(n_brr_sent); -+ return send_mh(daddr, saddr, MIPV6_MH_BRR, sizeof(br), (u8 *)&br, -+ NULL, NULL, ops, NULL); -+} -+ -+/** -+ * mipv6_send_ba - send a Binding Acknowledgement -+ * @saddr: source address for BA -+ * @daddr: destination address for BA -+ * @reply_coa: destination care-of address of MN -+ * @auth_coa: care-of address of MN used for authentication -+ * @status: status field value -+ * @sequence: sequence number from BU -+ * @lifetime: granted lifetime for binding in seconds -+ * @ops: mobility options -+ * -+ * Send a binding acknowledgement. On a mobile node, use the mobile -+ * node's home address for saddr. Returns 0 on success, non-zero on -+ * failure. -+ **/ -+int mipv6_send_ba(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *auth_coa, struct in6_addr *rep_coa, -+ u8 status, u16 sequence, u32 lifetime, u8 *k_bu) -+{ -+ struct mipv6_mh_ba ba; -+ struct mipv6_auth_parm parm; -+ struct mipv6_mh_opt *ops = NULL; -+ int ops_len = 0, ret = 0; -+ struct mipv6_bce bc_entry; -+ int coming_home = 0; -+ int bypass_tnl = 0; -+ -+ memset(&ba, 0, sizeof(ba)); -+ -+ ba.status = status; -+ ba.sequence = htons(sequence); -+ ba.lifetime = htons(lifetime >> 2); -+ -+ DEBUG(DBG_INFO, "sending a status %d BA %s authenticator to MN \n" -+ "%x:%x:%x:%x:%x:%x:%x:%x at care of address \n" -+ "%x:%x:%x:%x:%x:%x:%x:%x : with lifetime %d and \n" -+ " sequence number %d", -+ status, k_bu ? "with" : "without", -+ NIPV6ADDR(daddr), NIPV6ADDR(auth_coa), lifetime, sequence); -+ -+ memset(&parm, 0, sizeof(parm)); -+ parm.coa = auth_coa; -+ parm.cn_addr = saddr; -+ -+ if (k_bu) { -+ ops_len += sizeof(struct mipv6_mo_bauth_data) + -+ MIPV6_RR_MAC_LENGTH; -+ parm.k_bu = k_bu; -+ } -+ -+ if (mip6node_cnf.binding_refresh_advice) { -+ ops_len += sizeof(struct mipv6_mo_br_advice); -+ } -+ if (ops_len) { -+ ops = alloc_mh_opts(ops_len); -+ if (ops == NULL) { -+ DEBUG(DBG_WARNING, "Out of memory"); -+ return -ENOMEM; -+ } -+ if (mip6node_cnf.binding_refresh_advice > 0) { -+ if (append_mh_opt(ops, MIPV6_OPT_BIND_REFRESH_ADVICE, 2, -+ &mip6node_cnf.binding_refresh_advice) < 0) { -+ DEBUG(DBG_WARNING, "Adding BRA failed"); -+ if (ops) -+ kfree(ops); -+ return -ENOMEM; -+ } -+ } -+ if (k_bu) { -+ if (append_mh_opt(ops, MIPV6_OPT_AUTH_DATA, -+ MIPV6_RR_MAC_LENGTH, NULL) < 0) { -+ DEBUG(DBG_WARNING, "Adding BAD failed"); -+ if (ops) -+ kfree(ops); -+ return -ENOMEM; -+ } -+ } -+ } -+ coming_home = !ipv6_addr_cmp(rep_coa, daddr); -+ -+ bypass_tnl = (coming_home && -+ !mipv6_bcache_get(daddr, saddr, &bc_entry) && -+ bc_entry.flags&MIPV6_BU_F_HOME && -+ status >= 128); -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_del) -+ mip6_fn.bce_tnl_rt_del(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ if (coming_home) -+ ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba, -+ NULL, NULL, ops, &parm); -+ else -+ ret = send_mh(daddr, saddr, MIPV6_MH_BA, sizeof(ba), (u8 *)&ba, -+ NULL, rep_coa, ops, &parm); -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_add) -+ mip6_fn.bce_tnl_rt_add(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ if (ret == 0) { -+ if (status < 128) { -+ MIPV6_INC_STATS(n_ba_sent); -+ } else { -+ MIPV6_INC_STATS(n_ban_sent); -+ } -+ } -+ -+ if (ops) -+ kfree(ops); -+ -+ return 0; -+} -+ -+/** -+ * mipv6_send_be - send a Binding Error message -+ * @saddr: source address for BE -+ * @daddr: destination address for BE -+ * @home: Home Address in offending packet (if any) -+ * -+ * Sends a binding error. On a mobile node, use the mobile node's -+ * home address for @saddr. Returns 0 on success, negative on -+ * failure. -+ **/ -+int mipv6_send_be(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *home, __u8 status) -+{ -+ struct mipv6_mh_be be; -+ int ret = 0; -+ struct mipv6_bce bc_entry; -+ int bypass_tnl = 0; -+ -+ if (ipv6_addr_is_multicast(daddr)) -+ return -EINVAL; -+ -+ memset(&be, 0, sizeof(be)); -+ be.status = status; -+ if (home) -+ ipv6_addr_copy(&be.home_addr, home); -+ -+ if (mipv6_bcache_get(daddr, saddr, &bc_entry) == 0 && -+ bc_entry.flags&MIPV6_BU_F_HOME) -+ bypass_tnl = 1; -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_del) -+ mip6_fn.bce_tnl_rt_del(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ ret = send_mh(daddr, saddr, MIPV6_MH_BE, sizeof(be), (u8 *)&be, -+ NULL, NULL, NULL, NULL); -+ -+ if (bypass_tnl && mip6_fn.bce_tnl_rt_add) -+ mip6_fn.bce_tnl_rt_add(&bc_entry.coa, -+ &bc_entry.our_addr, -+ &bc_entry.home_addr); -+ -+ if (ret == 0) -+ MIPV6_INC_STATS(n_be_sent); -+ -+ return ret; -+} -+ -+/** -+ * mipv6_send_addr_test - send a HoT or CoT message -+ * @saddr: source address -+ * @daddr: destination address -+ * @msg_type: HoT or CoT message -+ * @init: HoTI or CoTI message -+ * -+ * Send a reply to HoTI or CoTI message. -+ **/ -+static int mipv6_send_addr_test(struct in6_addr *saddr, -+ struct in6_addr *daddr, -+ int msg_type, -+ struct mipv6_mh_addr_ti *init) -+{ -+ u_int8_t *kgen_token = NULL; -+ struct mipv6_mh_addr_test addr_test; -+ struct mipv6_rr_nonce *nonce; -+ struct mipv6_mh_opt *ops = NULL; -+ int ret = 0; -+ -+ DEBUG_FUNC(); -+ -+ if ((nonce = mipv6_rr_get_new_nonce())== NULL) { -+ DEBUG(DBG_WARNING, "Nonce creation failed"); -+ return 0; -+ } -+ if (mipv6_rr_cookie_create(daddr, &kgen_token, nonce->index)) { -+ DEBUG(DBG_WARNING, "No cookie"); -+ return 0; -+ } -+ -+ addr_test.nonce_index = nonce->index; -+ memcpy(addr_test.init_cookie, init->init_cookie, -+ MIPV6_RR_COOKIE_LENGTH); -+ memcpy(addr_test.kgen_token, kgen_token, -+ MIPV6_RR_COOKIE_LENGTH); -+ -+ /* No options defined */ -+ ret = send_mh(daddr, saddr, msg_type, sizeof(addr_test), -+ (u8 *)&addr_test, NULL, NULL, ops, NULL); -+ -+ if (ret == 0) { -+ if (msg_type == MIPV6_MH_HOT) { -+ MIPV6_INC_STATS(n_hot_sent); -+ } else { -+ MIPV6_INC_STATS(n_cot_sent); -+ } -+ } -+ -+ return 0; -+} -+ -+static void bc_cache_add(int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ __u8 ba_status = SUCCESS; -+ -+ if (lifetime > MAX_RR_BINDING_LIFE) -+ lifetime = MAX_RR_BINDING_LIFE; -+ -+ if (mipv6_bcache_add(ifindex, daddr, haddr, coa, lifetime, -+ sequence, flags, CACHE_ENTRY) != 0) { -+ DEBUG(DBG_ERROR, "binding failed."); -+ ba_status = INSUFFICIENT_RESOURCES; -+ } -+ -+ if (flags & MIPV6_BU_F_ACK) { -+ DEBUG(DBG_INFO, "sending ack (code=%d)", ba_status); -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, ba_status, sequence, -+ lifetime, k_bu); -+ } -+} -+ -+static void bc_cn_home_add(int ifindex, struct in6_addr *daddr, -+ struct in6_addr *haddr, struct in6_addr *coa, -+ struct in6_addr *rep_coa, __u32 lifetime, -+ __u16 sequence, __u8 flags, __u8 *k_bu) -+{ -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, -+ HOME_REGISTRATION_NOT_SUPPORTED, -+ sequence, lifetime, k_bu); -+} -+ -+static void bc_cache_delete(struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu) -+{ -+ __u8 status = SUCCESS; -+ -+ /* Cached Care-of Address Deregistration */ -+ if (mipv6_bcache_exists(haddr, daddr) == CACHE_ENTRY) { -+ mipv6_bcache_delete(haddr, daddr, CACHE_ENTRY); -+ } else { -+ DEBUG(DBG_INFO, "entry is not in cache"); -+ status = REASON_UNSPECIFIED; -+ } -+ if (flags & MIPV6_BU_F_ACK) { -+ mipv6_send_ba(daddr, haddr, coa, rep_coa, status, sequence, -+ 0, k_bu); -+ } -+} -+ -+static void bc_cn_home_delete(struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 sequence, __u8 flags, -+ __u8 *k_bu) -+{ -+} -+ -+/** -+ * parse_mo_tlv - Parse TLV-encoded Mobility Options -+ * @mos: pointer to Mobility Options -+ * @len: total length of options -+ * @opts: structure to store option pointers -+ * -+ * Parses Mobility Options passed in @mos. Stores pointers in @opts -+ * to all valid mobility options found in @mos. Unknown options and -+ * padding (%MIPV6_OPT_PAD1 and %MIPV6_OPT_PADN) is ignored and -+ * skipped. -+ **/ -+int parse_mo_tlv(void *mos, int len, struct mobopt *opts) -+{ -+ struct mipv6_mo *curr = (struct mipv6_mo *)mos; -+ int left = len; -+ -+ while (left > 0) { -+ int optlen = 0; -+ if (curr->type == MIPV6_OPT_PAD1) -+ optlen = 1; -+ else -+ optlen = 2 + curr->length; -+ -+ if (optlen > left) -+ goto bad; -+ -+ switch (curr->type) { -+ case MIPV6_OPT_PAD1: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_PAD1 at %x", curr); -+ break; -+ case MIPV6_OPT_PADN: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_PADN at %x", curr); -+ break; -+ case MIPV6_OPT_ALTERNATE_COA: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_ACOA at %x", curr); -+ opts->alt_coa = (struct mipv6_mo_alt_coa *)curr; -+ break; -+ case MIPV6_OPT_NONCE_INDICES: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_NONCE_INDICES at %x", curr); -+ opts->nonce_indices = -+ (struct mipv6_mo_nonce_indices *)curr; -+ break; -+ case MIPV6_OPT_AUTH_DATA: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_AUTH_DATA at %x", curr); -+ opts->auth_data = (struct mipv6_mo_bauth_data *)curr; -+ break; -+ case MIPV6_OPT_BIND_REFRESH_ADVICE: -+ DEBUG(DBG_DATADUMP, "MIPV6_OPT_BIND_REFRESH_ADVICE at %x", curr); -+ opts->br_advice = (struct mipv6_mo_br_advice *)curr; -+ break; -+ default: -+ DEBUG(DBG_INFO, "MO Unknown option type %d at %x, ignoring.", -+ curr->type, curr); -+ /* unknown mobility option, ignore and skip */ -+ } -+ -+ (u8 *)curr += optlen; -+ left -= optlen; -+ } -+ -+ if (left == 0) -+ return 0; -+ bad: -+ return -1; -+} -+ -+/* -+ * -+ * Mobility Header Message handlers -+ * -+ */ -+ -+static int mipv6_handle_mh_testinit(struct sk_buff *skb, -+ struct in6_addr *cn, -+ struct in6_addr *lcoa, -+ struct in6_addr *saddr, -+ struct in6_addr *fcoa, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_addr_ti *ti = (struct mipv6_mh_addr_ti *)mh->data; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ DEBUG_FUNC(); -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*ti); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "Mobility Header length less than H/C TestInit"); -+ return -1; -+ } -+ if (!mip6node_cnf.accept_ret_rout) { -+ DEBUG(DBG_INFO, "Return routability administratively disabled"); -+ return -1; -+ } -+ if (lcoa || fcoa) { -+ DEBUG(DBG_INFO, "H/C TestInit has HAO or RTH2, dropped."); -+ return -1; -+ } -+ -+ if (mh->type == MIPV6_MH_HOTI) { -+ MIPV6_INC_STATS(n_hoti_rcvd); -+ return mipv6_send_addr_test(cn, saddr, MIPV6_MH_HOT, ti); -+ } else if (mh->type == MIPV6_MH_COTI) { -+ MIPV6_INC_STATS(n_coti_rcvd); -+ return mipv6_send_addr_test(cn, saddr, MIPV6_MH_COT, ti); -+ } else -+ return -1; /* Impossible to get here */ -+} -+ -+/** -+ * mipv6_handle_mh_bu - Binding Update handler -+ * @src: care-of address of sender -+ * @dst: our address -+ * @haddr: home address of sender -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ * Handles Binding Update. Packet and offset to option are passed. -+ * Returns 0 on success, otherwise negative. -+ **/ -+static int mipv6_handle_mh_bu(struct sk_buff *skb, -+ struct in6_addr *dst, -+ struct in6_addr *unused, -+ struct in6_addr *haddr, -+ struct in6_addr *coaddr, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_bu *bu = (struct mipv6_mh_bu *)mh->data; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ int auth = 0; -+ int dereg; /* Is this deregistration? */ -+ int addr_type; -+ -+ struct mipv6_bce bc_entry; -+ struct in6_addr *coa, *reply_coa; -+ __u8 *key_bu = NULL; /* RR BU authentication key */ -+ __u8 flags = bu->flags; -+ __u16 sequence; -+ __u32 lifetime; -+ __u16 nonce_ind = (__u16) -1; -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*bu); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "Mobility Header length less than BU"); -+ MIPV6_INC_STATS(n_bu_drop.invalid); -+ return -1; -+ } -+ -+ addr_type = ipv6_addr_type(haddr); -+ if (addr_type&IPV6_ADDR_LINKLOCAL || !(addr_type&IPV6_ADDR_UNICAST)) -+ return -EINVAL; -+ -+ /* If HAO not present, CoA == HAddr */ -+ if (coaddr == NULL) -+ coa = haddr; -+ else { -+ coa = coaddr; -+ addr_type = ipv6_addr_type(coa); -+ if (addr_type&IPV6_ADDR_LINKLOCAL || -+ !(addr_type&IPV6_ADDR_UNICAST)) -+ return -EINVAL; -+ } -+ reply_coa = coa; -+ -+ sequence = ntohs(bu->sequence); -+ if (bu->lifetime == 0xffff) -+ lifetime = 0xffffffff; -+ else -+ lifetime = ntohs(bu->lifetime) << 2; -+ -+ dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0); -+ -+ if (opt_len > 0) { -+ struct mobopt opts; -+ memset(&opts, 0, sizeof(opts)); -+ if (parse_mo_tlv(bu + 1, opt_len, &opts) < 0) { -+ MIPV6_INC_STATS(n_bu_drop.invalid); -+ return -1; -+ } -+ /* -+ * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_NONCE_INDICES, -+ * MIPV6_OPT_ALT_COA -+ */ -+ if (opts.alt_coa) { -+ coa = &opts.alt_coa->addr; -+ dereg = (ipv6_addr_cmp(haddr, coa) == 0 || lifetime == 0); -+ } -+ addr_type = ipv6_addr_type(coa); -+ if (addr_type&IPV6_ADDR_LINKLOCAL || -+ !(addr_type&IPV6_ADDR_UNICAST)) -+ return -EINVAL; -+ -+ if (flags & MIPV6_BU_F_HOME) { -+ if (opts.nonce_indices) -+ return -1; -+ } else { -+ u8 ba_status = 0; -+ u8 *h_ckie = NULL, *c_ckie = NULL; /* Home and care-of cookies */ -+ -+ /* BUs to CN MUST include authorization data and nonce indices options */ -+ if (!opts.auth_data || !opts.nonce_indices) { -+ DEBUG(DBG_WARNING, -+ "Route optimization BU without authorization material, aborting processing"); -+ return MH_AUTH_FAILED; -+ } -+ if (mipv6_rr_cookie_create( -+ haddr, &h_ckie, opts.nonce_indices->home_nonce_i) < 0) { -+ DEBUG(DBG_WARNING, -+ "mipv6_rr_cookie_create failed for home cookie"); -+ ba_status = EXPIRED_HOME_NONCE_INDEX; -+ } -+ nonce_ind = opts.nonce_indices->home_nonce_i; -+ /* Don't create the care-of cookie, if MN deregisters */ -+ if (!dereg && mipv6_rr_cookie_create( -+ coa, &c_ckie, -+ opts.nonce_indices->careof_nonce_i) < 0) { -+ DEBUG(DBG_WARNING, -+ "mipv6_rr_cookie_create failed for coa cookie"); -+ if (ba_status == 0) -+ ba_status = EXPIRED_CAREOF_NONCE_INDEX; -+ else -+ ba_status = EXPIRED_NONCES; -+ } -+ if (ba_status == 0) { -+ if (dereg) -+ key_bu = mipv6_rr_key_calc(h_ckie, NULL); -+ else -+ key_bu = mipv6_rr_key_calc(h_ckie, c_ckie); -+ mh->checksum = 0;/* TODO: Don't mangle the packet */ -+ if (key_bu && mipv6_auth_check( -+ dst, coa, (__u8 *)mh, msg_len + sizeof(*mh), opts.auth_data, key_bu) == 0) { -+ DEBUG(DBG_INFO, "mipv6_auth_check OK for BU"); -+ auth = 1; -+ } else { -+ DEBUG(DBG_WARNING, -+ "BU Authentication failed"); -+ } -+ } -+ if (h_ckie) -+ kfree(h_ckie); -+ if (c_ckie) -+ kfree(c_ckie); -+ if (ba_status != 0) { -+ MIPV6_INC_STATS(n_bu_drop.auth); -+ mipv6_send_ba(dst, haddr, coa, -+ reply_coa, ba_status, -+ sequence, 0, NULL); -+ goto out; -+ } -+ } -+ -+ } -+ /* Require authorization option for RO, home reg is protected by IPsec */ -+ if (!(flags & MIPV6_BU_F_HOME) && !auth) { -+ MIPV6_INC_STATS(n_bu_drop.auth); -+ if (key_bu) -+ kfree(key_bu); -+ return MH_AUTH_FAILED; -+ } -+ -+ if (mipv6_bcache_get(haddr, dst, &bc_entry) == 0) { -+ if ((bc_entry.flags&MIPV6_BU_F_HOME) != -+ (flags&MIPV6_BU_F_HOME)) { -+ DEBUG(DBG_INFO, -+ "Registration type change. Sending BA REG_TYPE_CHANGE_FORBIDDEN"); -+ mipv6_send_ba(dst, haddr, coa, reply_coa, -+ REG_TYPE_CHANGE_FORBIDDEN, -+ sequence, lifetime, key_bu); -+ goto out; -+ } -+ if (!MIPV6_SEQ_GT(sequence, bc_entry.seq)) { -+ DEBUG(DBG_INFO, -+ "Sequence number mismatch. Sending BA SEQUENCE_NUMBER_OUT_OF_WINDOW"); -+ mipv6_send_ba(dst, haddr, coa, reply_coa, -+ SEQUENCE_NUMBER_OUT_OF_WINDOW, -+ bc_entry.seq, lifetime, key_bu); -+ goto out; -+ } -+ } -+ -+ if (!dereg) { -+ int ifindex; -+ struct rt6_info *rt; -+ -+ /* Avoid looping binding cache entries */ -+ if (mipv6_bcache_get(coa, dst, &bc_entry) == 0) { -+ DEBUG(DBG_WARNING, "Looped BU, dropping the packet"); -+ goto out; -+ } -+ DEBUG(DBG_INFO, "calling bu_add."); -+ if ((rt = rt6_lookup(haddr, dst, 0, 0)) != NULL) { -+ ifindex = rt->rt6i_dev->ifindex; -+ dst_release(&rt->u.dst); -+ } else { -+ /* -+ * Can't process the BU since the right interface is -+ * not found. -+ */ -+ DEBUG(DBG_WARNING, "No route entry found for handling " -+ "a BU request, (using 0 as index)"); -+ ifindex = 0; -+ } -+ if (flags & MIPV6_BU_F_HOME) -+ mip6_fn.bce_home_add(ifindex, dst, haddr, coa, -+ reply_coa, lifetime, sequence, -+ flags, key_bu); -+ else -+ mip6_fn.bce_cache_add(ifindex, dst, haddr, coa, -+ reply_coa, lifetime, sequence, -+ flags, key_bu); -+ } else { -+ DEBUG(DBG_INFO, "calling BCE delete."); -+ -+ if (flags & MIPV6_BU_F_HOME) -+ mip6_fn.bce_home_del(dst, haddr, coa, reply_coa, -+ sequence, flags, key_bu); -+ else { -+ mipv6_rr_invalidate_nonce(nonce_ind); -+ mip6_fn.bce_cache_del(dst, haddr, coa, reply_coa, -+ sequence, flags, key_bu); -+ } -+ } -+ out: -+ MIPV6_INC_STATS(n_bu_rcvd); -+ if (key_bu) -+ kfree(key_bu); -+ return 0; -+} -+ -+static int mipv6_mh_rcv(struct sk_buff *skb) -+{ -+ struct inet6_skb_parm *opt = (struct inet6_skb_parm *)skb->cb; -+ struct mipv6_mh *mh; -+ struct in6_addr *lhome, *fhome, *lcoa = NULL, *fcoa = NULL; -+ int ret = 0; -+ -+ fhome = &skb->nh.ipv6h->saddr; -+ lhome = &skb->nh.ipv6h->daddr; -+ -+ if (opt->hao != 0) { -+ struct mipv6_dstopt_homeaddr *hao; -+ hao = (struct mipv6_dstopt_homeaddr *)(skb->nh.raw + opt->hao); -+ fcoa = &hao->addr; -+ } -+ -+ if (opt->srcrt2 != 0) { -+ struct rt2_hdr *rt2; -+ rt2 = (struct rt2_hdr *)((u8 *)skb->nh.raw + opt->srcrt2); -+ lcoa = &rt2->addr; -+ } -+ -+ /* Verify checksum is correct */ -+ if (skb->ip_summed == CHECKSUM_HW) { -+ skb->ip_summed = CHECKSUM_UNNECESSARY; -+ if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY, -+ skb->csum)) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING "MIPv6 MH hw checksum failed\n"); -+ skb->ip_summed = CHECKSUM_NONE; -+ } -+ } -+ if (skb->ip_summed == CHECKSUM_NONE) { -+ if (csum_ipv6_magic(fhome, lhome, skb->len, IPPROTO_MOBILITY, -+ skb_checksum(skb, 0, skb->len, 0))) { -+ if (net_ratelimit()) -+ printk(KERN_WARNING "MIPv6 MH checksum failed\n"); -+ goto bad; -+ } -+ } -+ -+ if (!pskb_may_pull(skb, skb->h.raw-skb->data+sizeof(*mh)) || -+ !pskb_may_pull(skb, -+ skb->h.raw-skb->data+((skb->h.raw[1]+1)<<3))) { -+ DEBUG(DBG_INFO, "MIPv6 MH invalid length"); -+ kfree_skb(skb); -+ return 0; -+ } -+ -+ mh = (struct mipv6_mh *) skb->h.raw; -+ -+ /* Verify there are no more headers after the MH */ -+ if (mh->payload != NEXTHDR_NONE) { -+ __u32 pos = (__u32)&mh->payload - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "MIPv6 MH error"); -+ goto bad; -+ } -+ -+ if (mh->type > MIPV6_MH_MAX) { -+ /* send binding error */ -+ printk("Invalid mobility header type (%d)\n", mh->type); -+ mipv6_send_be(lhome, fcoa ? fcoa : fhome, -+ fcoa ? fhome : NULL, -+ MIPV6_BE_UNKNOWN_MH_TYPE); -+ goto bad; -+ } -+ if (mh_rcv[mh->type].func != NULL) { -+ ret = mh_rcv[mh->type].func(skb, lhome, lcoa, fhome, fcoa, mh); -+ } else { -+ DEBUG(DBG_INFO, "No handler for MH Type %d", mh->type); -+ goto bad; -+ } -+ -+ kfree_skb(skb); -+ return 0; -+ -+bad: -+ MIPV6_INC_STATS(n_mh_in_error); -+ kfree_skb(skb); -+ return 0; -+ -+} -+ -+#if LINUX_VERSION_CODE >= 0x2052a -+struct inet6_protocol mipv6_mh_protocol = -+{ -+ mipv6_mh_rcv, /* handler */ -+ NULL /* error control */ -+}; -+#else -+struct inet6_protocol mipv6_mh_protocol = -+{ -+ mipv6_mh_rcv, /* handler */ -+ NULL, /* error control */ -+ NULL, /* next */ -+ IPPROTO_MOBILITY, /* protocol ID */ -+ 0, /* copy */ -+ NULL, /* data */ -+ "MIPv6 MH" /* name */ -+}; -+#endif -+ -+/* -+ * -+ * Code module init/exit functions -+ * -+ */ -+ -+int __init mipv6_mh_common_init(void) -+{ -+ struct sock *sk; -+ int err; -+ -+ mip6_fn.bce_home_add = bc_cn_home_add; -+ mip6_fn.bce_cache_add = bc_cache_add; -+ mip6_fn.bce_home_del = bc_cn_home_delete; -+ mip6_fn.bce_cache_del = bc_cache_delete; -+ -+ mipv6_mh_socket = sock_alloc(); -+ if (mipv6_mh_socket == NULL) { -+ printk(KERN_ERR -+ "Failed to create the MIP6 MH control socket.\n"); -+ return -1; -+ } -+ mipv6_mh_socket->type = SOCK_RAW; -+ -+ if ((err = sock_create(PF_INET6, SOCK_RAW, IPPROTO_MOBILITY, -+ &mipv6_mh_socket)) < 0) { -+ printk(KERN_ERR -+ "Failed to initialize the MIP6 MH control socket (err %d).\n", -+ err); -+ sock_release(mipv6_mh_socket); -+ mipv6_mh_socket = NULL; /* for safety */ -+ return err; -+ } -+ -+ sk = mipv6_mh_socket->sk; -+ sk->allocation = GFP_ATOMIC; -+ sk->sndbuf = 64 * 1024 + sizeof(struct sk_buff); -+ sk->prot->unhash(sk); -+ -+ memset(&mh_rcv, 0, sizeof(mh_rcv)); -+ mh_rcv[MIPV6_MH_HOTI].func = mipv6_handle_mh_testinit; -+ mh_rcv[MIPV6_MH_COTI].func = mipv6_handle_mh_testinit; -+ mh_rcv[MIPV6_MH_BU].func = mipv6_handle_mh_bu; -+ -+#if LINUX_VERSION_CODE >= 0x2052a -+ if (inet6_add_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY) < 0) { -+ printk(KERN_ERR "Failed to register MOBILITY protocol\n"); -+ sock_release(mipv6_mh_socket); -+ mipv6_mh_socket = NULL; -+ return -EAGAIN; -+ } -+#else -+ inet6_add_protocol(&mipv6_mh_protocol); -+#endif -+ /* To disable the use of dst_cache, -+ * which slows down the sending of BUs ?? -+ */ -+ sk->dst_cache=NULL; -+ -+ return 0; -+} -+ -+void __exit mipv6_mh_common_exit(void) -+{ -+ if (mipv6_mh_socket) sock_release(mipv6_mh_socket); -+ mipv6_mh_socket = NULL; /* For safety. */ -+ -+#if LINUX_VERSION_CODE >= 0x2052a -+ inet6_del_protocol(&mipv6_mh_protocol, IPPROTO_MOBILITY); -+#else -+ inet6_del_protocol(&mipv6_mh_protocol); -+#endif -+ memset(&mh_rcv, 0, sizeof(mh_rcv)); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/mobhdr_mn.c -@@ -0,0 +1,1155 @@ -+/* -+ * Mobile IPv6 Mobility Header Functions for Mobile Node -+ * -+ * Authors: -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * Niklas Kämpe <nhkampe@cc.hut.fi> -+ * Henrik Petander <henrik.petander@hut.fi> -+ * -+ * $Id:$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License as -+ * published by the Free Software Foundation; either version 2 of -+ * the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/types.h> -+#include <linux/sched.h> -+#include <linux/init.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "mobhdr.h" -+#include "mn.h" -+#include "bul.h" -+#include "rr_crypto.h" -+#include "debug.h" -+#include "util.h" -+#include "stats.h" -+ -+int rr_configured = 1; -+ -+/* Return value of mipv6_rr_state() */ -+#define NO_RR 0 -+#define DO_RR 1 -+#define RR_FOR_COA 2 -+#define INPROGRESS_RR 3 -+ -+/** -+ * send_bu_msg - sends a Binding Update -+ * @bulentry : BUL entry with the information for building a BU -+ * -+ * Function builds a BU msg based on the contents of a bul entry. -+ * Does not change the bul entry. -+ **/ -+static int send_bu_msg(struct mipv6_bul_entry *binding) -+{ -+ int auth = 0; /* Use auth */ -+ int ret = 0; -+ struct mipv6_auth_parm parm; -+ struct mipv6_mh_bu bu; -+ -+ if (!binding) { -+ DEBUG(DBG_ERROR, "called with a null bul entry"); -+ return -1; -+ } -+ -+ memset(&parm, 0, sizeof(parm)); -+ if (mipv6_prefix_compare(&binding->coa, &binding->home_addr, 64)) -+ parm.coa = &binding->home_addr; -+ else -+ parm.coa = &binding->coa; -+ parm.cn_addr = &binding->cn_addr; -+ -+ if (binding->rr && binding->rr->kbu) { -+ DEBUG(DBG_INFO, "Binding with key"); -+ auth = 1; -+ parm.k_bu = binding->rr->kbu; -+ } -+ memset(&bu, 0, sizeof(bu)); -+ bu.flags = binding->flags; -+ bu.sequence = htons(binding->seq); -+ bu.lifetime = htons(binding->lifetime >> 2); -+ bu.reserved = 0; -+ -+ ret = send_mh(&binding->cn_addr, &binding->home_addr, -+ MIPV6_MH_BU, sizeof(bu), (u8 *)&bu, -+ &binding->home_addr, NULL, -+ binding->ops, &parm); -+ -+ if (ret == 0) -+ MIPV6_INC_STATS(n_bu_sent); -+ -+ return ret; -+} -+ -+/** -+ * mipv6_send_addr_test_init - send a HoTI or CoTI message -+ * @saddr: source address for H/CoTI -+ * @daddr: destination address for H/CoTI -+ * @msg_type: Identifies whether HoTI or CoTI -+ * @init_cookie: the HoTi or CoTi init cookie -+ * -+ * The message will be retransmitted till we get a HoT or CoT message, since -+ * our caller (mipv6_RR_start) has entered this message in the BUL with -+ * exponential backoff retramission set. -+ */ -+static int mipv6_send_addr_test_init(struct in6_addr *saddr, -+ struct in6_addr *daddr, -+ u8 msg_type, -+ u8 *init_cookie) -+{ -+ struct mipv6_mh_addr_ti ti; -+ struct mipv6_mh_opt *ops = NULL; -+ int ret = 0; -+ -+ /* Set reserved and copy the cookie from address test init msg */ -+ ti.reserved = 0; -+ mipv6_rr_mn_cookie_create(init_cookie); -+ memcpy(ti.init_cookie, init_cookie, MIPV6_RR_COOKIE_LENGTH); -+ -+ ret = send_mh(daddr, saddr, msg_type, sizeof(ti), (u8 *)&ti, -+ NULL, NULL, ops, NULL); -+ if (ret == 0) { -+ if (msg_type == MIPV6_MH_HOTI) { -+ MIPV6_INC_STATS(n_hoti_sent); -+ } else { -+ MIPV6_INC_STATS(n_coti_sent); -+ } -+ } -+ -+ return ret; -+} -+ -+/* -+ * -+ * Callback handlers for binding update list -+ * -+ */ -+ -+/* Return value 0 means keep entry, non-zero means discard entry. */ -+ -+/* Callback for BUs not requiring acknowledgement -+ */ -+int bul_entry_expired(struct mipv6_bul_entry *bulentry) -+{ -+ /* Lifetime expired, delete entry. */ -+ DEBUG(DBG_INFO, "bul entry 0x%p lifetime expired, deleting entry", -+ bulentry); -+ return 1; -+} -+ -+/* Callback for BUs requiring acknowledgement with exponential resending -+ * scheme */ -+static int bul_resend_exp(struct mipv6_bul_entry *bulentry) -+{ -+ unsigned long now = jiffies; -+ -+ DEBUG(DBG_INFO, "(0x%x) resending bu", (int) bulentry); -+ -+ -+ /* If sending a de-registration, do not care about the -+ * lifetime value, as de-registrations are normally sent with -+ * a zero lifetime value. If the entry is a home entry get the -+ * current lifetime. -+ */ -+ -+ if (bulentry->lifetime != 0) { -+ bulentry->lifetime = mipv6_mn_get_bulifetime( -+ &bulentry->home_addr, &bulentry->coa, bulentry->flags); -+ -+ bulentry->expire = now + bulentry->lifetime * HZ; -+ } else { -+ bulentry->expire = now + HOME_RESEND_EXPIRE * HZ; -+ } -+ if (bulentry->rr) { -+ /* Redo RR, if cookies have expired */ -+ if (time_after(jiffies, bulentry->rr->home_time + MAX_TOKEN_LIFE * HZ)) -+ bulentry->rr->rr_state |= RR_WAITH; -+ if (time_after(jiffies, bulentry->rr->careof_time + MAX_NONCE_LIFE * HZ)) -+ bulentry->rr->rr_state |= RR_WAITC; -+ -+ if (bulentry->rr->rr_state & RR_WAITH) { -+ /* Resend HoTI directly */ -+ mipv6_send_addr_test_init(&bulentry->home_addr, -+ &bulentry->cn_addr, MIPV6_MH_HOTI, -+ bulentry->rr->hot_cookie); -+ } -+ if (bulentry->rr->rr_state & RR_WAITC) { -+ /* Resend CoTI directly */ -+ mipv6_send_addr_test_init(&bulentry->coa, -+ &bulentry->cn_addr, MIPV6_MH_COTI, -+ bulentry->rr->cot_cookie); -+ } -+ goto out; -+ } -+ -+ bulentry->seq++; -+ -+ if (send_bu_msg(bulentry) < 0) -+ DEBUG(DBG_ERROR, "Resending of BU failed"); -+ -+out: -+ /* Schedule next retransmission */ -+ if (bulentry->delay < bulentry->maxdelay) { -+ bulentry->delay = 2 * bulentry->delay; -+ if (bulentry->delay > bulentry->maxdelay) { -+ /* can happen if maxdelay is not power(mindelay, 2) */ -+ bulentry->delay = bulentry->maxdelay; -+ } -+ } else if (bulentry->flags & MIPV6_BU_F_HOME) { -+ /* Home registration - continue sending BU at maxdelay rate */ -+ DEBUG(DBG_INFO, "Sending BU to HA after max ack wait time " -+ "reached(0x%x)", (int) bulentry); -+ bulentry->delay = bulentry->maxdelay; -+ } else if (!(bulentry->flags & MIPV6_BU_F_HOME)) { -+ /* Failed to get BA from a CN */ -+ bulentry->callback_time = now; -+ return -1; -+ } -+ -+ bulentry->callback_time = now + bulentry->delay * HZ; -+ return 0; -+} -+ -+ -+ -+/* Callback for sending a registration refresh BU -+ */ -+static int bul_refresh(struct mipv6_bul_entry *bulentry) -+{ -+ unsigned long now = jiffies; -+ -+ /* Refresh interval passed, send new BU */ -+ DEBUG(DBG_INFO, "bul entry 0x%x refresh interval passed, sending new BU", (int) bulentry); -+ if (bulentry->lifetime == 0) -+ return 0; -+ -+ /* Set new maximum lifetime and expiration time */ -+ bulentry->lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, -+ &bulentry->coa, -+ bulentry->flags); -+ bulentry->expire = now + bulentry->lifetime * HZ; -+ bulentry->seq++; -+ /* Send update */ -+ if (send_bu_msg(bulentry) < 0) -+ DEBUG(DBG_ERROR, "Resending of BU failed"); -+ -+ if (time_after_eq(now, bulentry->expire)) { -+ /* Sanity check */ -+ DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs", ERROR_DEF_LIFETIME); -+ bulentry->lifetime = ERROR_DEF_LIFETIME; -+ bulentry->expire = now + ERROR_DEF_LIFETIME*HZ; -+ } -+ -+ /* Set up retransmission */ -+ bulentry->state = RESEND_EXP; -+ bulentry->callback = bul_resend_exp; -+ bulentry->callback_time = now + INITIAL_BINDACK_TIMEOUT*HZ; -+ bulentry->delay = INITIAL_BINDACK_TIMEOUT; -+ bulentry->maxdelay = MAX_BINDACK_TIMEOUT; -+ -+ return 0; -+} -+ -+static int mipv6_send_RR_bu(struct mipv6_bul_entry *bulentry) -+{ -+ int ret; -+ int ops_len = 0; -+ u16 nonces[2]; -+ -+ DEBUG(DBG_INFO, "Sending BU to CN %x:%x:%x:%x:%x:%x:%x:%x " -+ "for home address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(&bulentry->cn_addr), NIPV6ADDR(&bulentry->home_addr)); -+ nonces[0] = bulentry->rr->home_nonce_index; -+ nonces[1] = bulentry->rr->careof_nonce_index; -+ ops_len = sizeof(struct mipv6_mo_bauth_data) + MIPV6_RR_MAC_LENGTH + -+ sizeof(struct mipv6_mo_nonce_indices); -+ if (bulentry->ops) { -+ DEBUG(DBG_WARNING, "Bul entry had existing mobility options, freeing them"); -+ kfree(bulentry->ops); -+ } -+ bulentry->ops = alloc_mh_opts(ops_len); -+ -+ if (!bulentry->ops) -+ return -ENOMEM; -+ if (append_mh_opt(bulentry->ops, MIPV6_OPT_NONCE_INDICES, -+ sizeof(struct mipv6_mo_nonce_indices) - 2, nonces) < 0) -+ return -ENOMEM; -+ -+ if (append_mh_opt(bulentry->ops, MIPV6_OPT_AUTH_DATA, -+ MIPV6_RR_MAC_LENGTH, NULL) < 0) -+ return -ENOMEM; -+ /* RR procedure is over, send a BU */ -+ if (!(bulentry->flags & MIPV6_BU_F_ACK)) { -+ DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired"); -+ bulentry->state = ACK_OK; -+ bulentry->callback = bul_entry_expired; -+ bulentry->callback_time = jiffies + HZ * bulentry->lifetime; -+ bulentry->expire = jiffies + HZ * bulentry->lifetime; -+ } -+ else { -+ bulentry->callback_time = jiffies + HZ; -+ bulentry->expire = jiffies + HZ * bulentry->lifetime; -+ } -+ -+ ret = send_bu_msg(bulentry); -+ mipv6_bul_reschedule(bulentry); -+ return ret; -+} -+ -+static int mipv6_rr_state(struct mipv6_bul_entry *bul, struct in6_addr *saddr, -+ struct in6_addr *coa, __u8 flags) -+{ -+ if (!rr_configured) -+ return NO_RR; -+ if (flags & MIPV6_BU_F_HOME) { -+ /* We don't need RR, this is a Home Registration */ -+ return NO_RR; -+ } -+ if (!bul || !bul->rr) { -+ /* First time BU to CN, need RR */ -+ return DO_RR; -+ } -+ -+ switch (bul->rr->rr_state) { -+ case RR_INIT: -+ /* Need RR if first BU to CN */ -+ return DO_RR; -+ case RR_DONE: -+ /* If MN moves to a new coa, do RR for it */ -+ if (!ipv6_addr_cmp(&bul->coa, coa)) -+ return NO_RR; -+ else -+ return DO_RR; -+ default: -+ /* -+ * We are in the middle of RR, the HoTI and CoTI have been -+ * sent. But we haven't got HoT and CoT from the CN, so -+ * don't do anything more at this time. -+ */ -+ return INPROGRESS_RR; -+ } -+} -+ -+/** -+ * mipv6_RR_start - Start Return Routability procedure -+ * @home_addr: home address -+ * @cn_addr: correspondent address -+ * @coa: care-of address -+ * @entry: binding update list entry (if any) -+ * @initdelay: initial ack timeout -+ * @maxackdelay: maximum ack timeout -+ * @flags: flags -+ * @lifetime: lifetime of binding -+ * @ops: mobility options -+ * -+ * Caller must hold @bul_lock (write). -+ **/ -+static int mipv6_RR_start(struct in6_addr *home_addr, struct in6_addr *cn_addr, -+ struct in6_addr *coa, struct mipv6_bul_entry *entry, -+ __u32 initdelay, __u32 maxackdelay, __u8 flags, -+ __u32 lifetime, struct mipv6_mh_opt *ops) -+{ -+ int ret = -1; -+ struct mipv6_bul_entry *bulentry = entry; -+ struct mipv6_rr_info *rr = NULL; -+ int seq = 0; -+ DEBUG_FUNC(); -+ -+ /* Do RR procedure only for care-of address after handoff, -+ if home cookie is still valid */ -+ if (bulentry && bulentry->rr) { -+ if (time_before(jiffies, bulentry->rr->home_time + MAX_NONCE_LIFE * HZ) && -+ lifetime && !(ipv6_addr_cmp(home_addr, coa) == 0)) { -+ mipv6_rr_mn_cookie_create(bulentry->rr->cot_cookie); -+ DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for CoA"); -+ ipv6_addr_copy(&bulentry->coa, coa); -+ bulentry->rr->rr_state |= RR_WAITC; -+ } else if (!lifetime) { /* Send only HoTi when returning home */ -+ mipv6_rr_mn_cookie_create(bulentry->rr->hot_cookie); -+ DEBUG(DBG_INFO, "Bul entry and rr info exist, only doing RR for HoA"); -+ ipv6_addr_copy(&bulentry->coa, coa); /* Home address as CoA */ -+ bulentry->rr->rr_state |= RR_WAITH; -+ } -+ } else { -+ DEBUG(DBG_INFO, "Doing RR for both HoA and CoA"); -+ rr = kmalloc(sizeof(*rr), GFP_ATOMIC); -+ memset(rr, 0, sizeof(*rr)); -+ rr->rr_state = RR_WAITHC; -+ } -+ if (bulentry) { -+ if (bulentry->state == ACK_ERROR) -+ goto out; -+ seq = bulentry->seq + 1; -+ } else -+ seq = 0; -+ /* Save the info in the BUL to retransmit the BU after RR is done */ -+ /* Caller must hold bul_lock (write) since we don't */ -+ -+ if ((bulentry = mipv6_bul_add(cn_addr, home_addr, coa, -+ min_t(__u32, lifetime, MAX_RR_BINDING_LIFE), -+ seq, flags, bul_resend_exp, initdelay, -+ RESEND_EXP, initdelay, -+ maxackdelay, ops, -+ rr)) == NULL) { -+ DEBUG(DBG_INFO, "couldn't update BUL for HoTi"); -+ goto out; -+ } -+ -+ rr = bulentry->rr; -+ if (rr->rr_state&RR_WAITH) -+ mipv6_send_addr_test_init(home_addr, cn_addr, MIPV6_MH_HOTI, -+ rr->hot_cookie); -+ if (ipv6_addr_cmp(home_addr, coa) && lifetime) -+ mipv6_send_addr_test_init(coa, cn_addr, MIPV6_MH_COTI, rr->cot_cookie); -+ else { -+ bulentry->rr->rr_state &= ~RR_WAITC; -+ } -+ ret = 0; -+out: -+ return ret; -+} -+ -+/* -+ * Status codes for mipv6_ba_rcvd() -+ */ -+#define STATUS_UPDATE 0 -+#define STATUS_REMOVE 1 -+ -+/** -+ * mipv6_ba_rcvd - Update BUL for this Binding Acknowledgement -+ * @ifindex: interface BA came from -+ * @cnaddr: sender IPv6 address -+ * @home_addr: home address -+ * @sequence: sequence number -+ * @lifetime: lifetime granted by Home Agent in seconds -+ * @refresh: recommended resend interval -+ * @status: %STATUS_UPDATE (ack) or %STATUS_REMOVE (nack) -+ * -+ * This function must be called to notify the module of the receipt of -+ * a binding acknowledgement so that it can cease retransmitting the -+ * option. The caller must have validated the acknowledgement before calling -+ * this function. 'status' can be either STATUS_UPDATE in which case the -+ * binding acknowledgement is assumed to be valid and the corresponding -+ * binding update list entry is updated, or STATUS_REMOVE in which case -+ * the corresponding binding update list entry is removed (this can be -+ * used upon receiving a negative acknowledgement). -+ * Returns 0 if a matching binding update has been sent or non-zero if -+ * not. -+ */ -+static int mipv6_ba_rcvd(int ifindex, struct in6_addr *cnaddr, -+ struct in6_addr *home_addr, -+ u16 sequence, u32 lifetime, -+ u32 refresh, int status) -+{ -+ struct mipv6_bul_entry *bulentry; -+ unsigned long now = jiffies; -+ struct in6_addr coa; -+ -+ DEBUG(DBG_INFO, "BA received with sequence number 0x%x, status: %d", -+ (int) sequence, status); -+ -+ /* Find corresponding entry in binding update list. */ -+ write_lock(&bul_lock); -+ if ((bulentry = mipv6_bul_get(cnaddr, home_addr)) == NULL) { -+ DEBUG(DBG_INFO, "- discarded, no entry in bul matches BA source address"); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ -+ ipv6_addr_copy(&coa, &bulentry->coa); -+ if (status == SEQUENCE_NUMBER_OUT_OF_WINDOW) { -+ __u32 lifetime = mipv6_mn_get_bulifetime(&bulentry->home_addr, -+ &bulentry->coa, -+ bulentry->flags); -+ bulentry->seq = sequence; -+ -+ mipv6_send_bu(&bulentry->home_addr, &bulentry->cn_addr, -+ &bulentry->coa, INITIAL_BINDACK_TIMEOUT, -+ MAX_BINDACK_TIMEOUT, 1, bulentry->flags, -+ lifetime, NULL); -+ write_unlock(&bul_lock); -+ return 0; -+ } else if (status >= REASON_UNSPECIFIED) { -+ int err; -+ int at_home = MN_NOT_AT_HOME; -+ DEBUG(DBG_WARNING, "- NACK - BA status: %d, deleting bul entry", status); -+ if (bulentry->flags & MIPV6_BU_F_HOME) { -+ struct mn_info *minfo; -+ read_lock(&mn_info_lock); -+ minfo = mipv6_mninfo_get_by_home(home_addr); -+ if (minfo) { -+ spin_lock(&minfo->lock); -+ if (minfo->is_at_home != MN_NOT_AT_HOME) -+ minfo->is_at_home = MN_AT_HOME; -+ at_home = minfo->is_at_home; -+ minfo->has_home_reg = 0; -+ spin_unlock(&minfo->lock); -+ } -+ read_unlock(&mn_info_lock); -+ DEBUG(DBG_ERROR, "Home registration failed: BA status: %d, deleting bul entry", status); -+ } -+ write_unlock(&bul_lock); -+ err = mipv6_bul_delete(cnaddr, home_addr); -+ if (at_home == MN_AT_HOME) { -+ mipv6_mn_send_home_na(home_addr); -+ write_lock_bh(&bul_lock); -+ mipv6_bul_iterate(mn_cn_handoff, &coa); -+ write_unlock_bh(&bul_lock); -+ } -+ return err; -+ } -+ bulentry->state = ACK_OK; -+ -+ if (bulentry->flags & MIPV6_BU_F_HOME && lifetime > 0) { -+ /* For home registrations: schedule a refresh binding update. -+ * Use the refresh interval given by home agent or 80% -+ * of lifetime, whichever is less. -+ * -+ * Adjust binding lifetime if 'granted' lifetime -+ * (lifetime value in received binding acknowledgement) -+ * is shorter than 'requested' lifetime (lifetime -+ * value sent in corresponding binding update). -+ * max((L_remain - (L_update - L_ack)), 0) -+ */ -+ if (lifetime * HZ < (bulentry->expire - bulentry->lastsend)) { -+ bulentry->expire = -+ max_t(__u32, bulentry->expire - -+ ((bulentry->expire - bulentry->lastsend) - -+ lifetime * HZ), jiffies + -+ ERROR_DEF_LIFETIME * HZ); -+ } -+ if (refresh > lifetime || refresh == 0) -+ refresh = 4 * lifetime / 5; -+ DEBUG(DBG_INFO, "setting callback for expiration of" -+ " a Home Registration: lifetime:%d, refresh:%d", -+ lifetime, refresh); -+ bulentry->callback = bul_refresh; -+ bulentry->callback_time = now + refresh * HZ; -+ bulentry->expire = now + lifetime * HZ; -+ bulentry->lifetime = lifetime; -+ if (time_after_eq(jiffies, bulentry->expire)) { -+ /* Sanity check */ -+ DEBUG(DBG_ERROR, "bul entry expire time in history - setting expire to %u secs", -+ ERROR_DEF_LIFETIME); -+ bulentry->expire = jiffies + ERROR_DEF_LIFETIME * HZ; -+ } -+ mipv6_mn_set_home_reg(home_addr, 1); -+ mipv6_bul_iterate(mn_cn_handoff, &coa); -+ } else if ((bulentry->flags & MIPV6_BU_F_HOME) && bulentry->lifetime == 0) { -+ write_unlock(&bul_lock); -+ DEBUG(DBG_INFO, "Got BA for deregistration BU"); -+ mipv6_mn_set_home_reg(home_addr, 0); -+ mipv6_bul_delete(cnaddr, home_addr); -+ mipv6_mn_send_home_na(home_addr); -+ -+ write_lock_bh(&bul_lock); -+ mipv6_bul_iterate(mn_cn_handoff, &coa); -+ write_unlock_bh(&bul_lock); -+ return 0; -+ } -+ -+ mipv6_bul_reschedule(bulentry); -+ write_unlock(&bul_lock); -+ -+ return 0; -+} -+ -+static int mipv6_handle_mh_HC_test(struct sk_buff *skb, -+ struct in6_addr *saddr, -+ struct in6_addr *fcoa, -+ struct in6_addr *cn, -+ struct in6_addr *lcoa, -+ struct mipv6_mh *mh) -+{ -+ int ret = 0; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ -+ struct mipv6_mh_addr_test *tm = (struct mipv6_mh_addr_test *)mh->data; -+ struct mipv6_bul_entry *bulentry; -+ -+ DEBUG_FUNC(); -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*tm); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_INFO, "Mobility Header length less than H/C Test"); -+ return -1; -+ } -+ if (fcoa || lcoa) { -+ DEBUG(DBG_INFO, "H/C Test has HAO or RTH2, dropped."); -+ return -1; -+ } -+ write_lock(&bul_lock); -+ -+ /* We need to get the home address, since CoT only has the CoA*/ -+ if (mh->type == MIPV6_MH_COT) { -+ if ((bulentry = mipv6_bul_get_by_ccookie(cn, tm->init_cookie)) == NULL) { -+ DEBUG(DBG_ERROR, "has no BUL or RR state for " -+ "source:%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(cn)); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ } else { /* HoT has the home address */ -+ if (((bulentry = mipv6_bul_get(cn, saddr)) == NULL) || !bulentry->rr) { -+ DEBUG(DBG_ERROR, "has no BUL or RR state for " -+ "source:%x:%x:%x:%x:%x:%x:%x:%x " -+ "dest:%x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(cn), NIPV6ADDR(saddr)); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ } -+ -+ switch (mh->type) { -+ case MIPV6_MH_HOT: -+ if ((bulentry->rr->rr_state & RR_WAITH) == 0) { -+ DEBUG(DBG_ERROR, "Not waiting for a Home Test message"); -+ goto out; -+ } -+ /* -+ * Make sure no home cookies have been received yet. -+ * TODO: Check not being put in at this time since subsequent -+ * BU's after this time will have home cookie stored. -+ */ -+ -+ /* Check if the cookie received is the right one */ -+ if (!mipv6_equal_cookies(tm->init_cookie, -+ bulentry->rr->hot_cookie)) { -+ /* Invalid cookie, might be an old cookie */ -+ DEBUG(DBG_WARNING, "Received HoT cookie does not match stored cookie"); -+ goto out; -+ } -+ DEBUG(DBG_INFO, "Got Care-of Test message"); -+ bulentry->rr->rr_state &= ~RR_WAITH; -+ memcpy(bulentry->rr->home_cookie, tm->kgen_token, MIPV6_COOKIE_LEN); -+ bulentry->rr->home_nonce_index = tm->nonce_index; -+ bulentry->rr->home_time = jiffies; -+ ret = 1; -+ break; -+ -+ case MIPV6_MH_COT: -+ if ((bulentry->rr->rr_state & RR_WAITC) == 0) { -+ DEBUG(DBG_ERROR, "Not waiting for a Home Test message"); -+ goto out; -+ } -+ /* -+ * Make sure no home cookies have been received yet. -+ * TODO: Check not being put in at this time since subsequent -+ * BU's at this time will have careof cookie stored. -+ */ -+ -+ /* Check if the cookie received is the right one */ -+ if (!mipv6_equal_cookies(tm->init_cookie, -+ bulentry->rr->cot_cookie)) { -+ DEBUG(DBG_INFO, "Received CoT cookie does not match stored cookie"); -+ goto out; -+ } -+ bulentry->rr->rr_state &= ~RR_WAITC; -+ memcpy(bulentry->rr->careof_cookie, tm->kgen_token, MIPV6_COOKIE_LEN); -+ bulentry->rr->careof_nonce_index = tm->nonce_index; -+ bulentry->rr->careof_time = jiffies; -+ ret = 1; -+ break; -+ default: -+ /* Impossible to get here */ -+ break; -+ } -+out: -+ if (bulentry->rr->rr_state == RR_DONE) { -+ if (bulentry->rr->kbu) /* First free any old keys */ -+ kfree(bulentry->rr->kbu); -+ /* Store the session key to be used in BU's */ -+ if (ipv6_addr_cmp(&bulentry->coa, &bulentry->home_addr) && bulentry->lifetime) -+ bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie, -+ bulentry->rr->careof_cookie); -+ else -+ bulentry->rr->kbu = mipv6_rr_key_calc(bulentry->rr->home_cookie, -+ NULL); -+ /* RR procedure is over, send a BU */ -+ mipv6_send_RR_bu(bulentry); -+ } -+ write_unlock(&bul_lock); -+ return ret; -+} -+ -+/** -+ * mipv6_handle_mh_brr - Binding Refresh Request handler -+ * @home: home address -+ * @coa: care-of address -+ * @cn: source of this packet -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ * Handles Binding Refresh Request. Packet and offset to option are -+ * passed. Returns 0 on success, otherwise negative. -+ **/ -+static int mipv6_handle_mh_brr(struct sk_buff *skb, -+ struct in6_addr *home, -+ struct in6_addr *unused1, -+ struct in6_addr *cn, -+ struct in6_addr *unused2, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_brr *brr = (struct mipv6_mh_brr *)mh->data; -+ struct mipv6_bul_entry *binding; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*brr); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_WARNING, "Mobility Header length less than BRR"); -+ MIPV6_INC_STATS(n_brr_drop.invalid); -+ return -1; -+ } -+ -+ /* check we know src, else drop */ -+ write_lock(&bul_lock); -+ if ((binding = mipv6_bul_get(cn, home)) == NULL) { -+ MIPV6_INC_STATS(n_brr_drop.misc); -+ write_unlock(&bul_lock); -+ return MH_UNKNOWN_CN; -+ } -+ -+ MIPV6_INC_STATS(n_brr_rcvd); -+ -+ if (opt_len > 0) { -+ struct mobopt opts; -+ memset(&opts, 0, sizeof(opts)); -+ if (parse_mo_tlv(brr + 1, opt_len, &opts) < 0) { -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ /* -+ * MIPV6_OPT_AUTH_DATA -+ */ -+ } -+ -+ /* must hold bul_lock (write) */ -+ mipv6_RR_start(home, cn, &binding->coa, binding, binding->delay, -+ binding->maxdelay, binding->flags, -+ binding->lifetime, binding->ops); -+ -+ write_unlock(&bul_lock); -+ /* MAY also decide to delete binding and send zero lifetime BU -+ with alt-coa set to home address */ -+ -+ return 0; -+} -+ -+/** -+ * mipv6_handle_mh_ba - Binding Acknowledgement handler -+ * @src: source of this packet -+ * @coa: care-of address -+ * @home: home address -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ **/ -+static int mipv6_handle_mh_ba(struct sk_buff *skb, -+ struct in6_addr *home, -+ struct in6_addr *coa, -+ struct in6_addr *src, -+ struct in6_addr *unused, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_ba *ba = (struct mipv6_mh_ba *)mh->data; -+ struct mipv6_bul_entry *binding = NULL; -+ struct mobopt opts; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ -+ int auth = 1, req_auth = 1, refresh = -1, ifindex = 0; -+ u32 lifetime, sequence; -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*ba); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_WARNING, "Mobility Header length less than BA"); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ return -1; -+ } -+ -+ lifetime = ntohs(ba->lifetime) << 2; -+ sequence = ntohs(ba->sequence); -+ -+ if (opt_len > 0) { -+ memset(&opts, 0, sizeof(opts)); -+ if (parse_mo_tlv(ba + 1, opt_len, &opts) < 0) -+ return -1; -+ /* -+ * MIPV6_OPT_AUTH_DATA, MIPV6_OPT_BR_ADVICE -+ */ -+ if (opts.br_advice) -+ refresh = ntohs(opts.br_advice->refresh_interval); -+ } -+ -+ if (ba->status >= EXPIRED_HOME_NONCE_INDEX && -+ ba->status <= EXPIRED_NONCES) -+ req_auth = 0; -+ -+ write_lock(&bul_lock); -+ binding = mipv6_bul_get(src, home); -+ if (!binding) { -+ DEBUG(DBG_INFO, "No binding, BA dropped."); -+ write_unlock(&bul_lock); -+ return -1; -+ } -+ -+ if (opts.auth_data && binding->rr && -+ (mipv6_auth_check(src, coa, (__u8 *)mh, msg_len, -+ opts.auth_data, binding->rr->kbu) == 0)) -+ auth = 1; -+ -+ if (req_auth && binding->rr && !auth) { -+ DEBUG(DBG_INFO, "BA Authentication failed."); -+ MIPV6_INC_STATS(n_ba_drop.auth); -+ write_unlock(&bul_lock); -+ return MH_AUTH_FAILED; -+ } -+ -+ if (ba->status == SEQUENCE_NUMBER_OUT_OF_WINDOW) { -+ DEBUG(DBG_INFO, -+ "Sequence number out of window, setting seq to %d", -+ sequence); -+ } else if (binding->seq != sequence) { -+ DEBUG(DBG_INFO, "BU/BA Sequence Number mismatch %d != %d", -+ binding->seq, sequence); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ write_unlock(&bul_lock); -+ return MH_SEQUENCE_MISMATCH; -+ } -+ if (ba->status == EXPIRED_HOME_NONCE_INDEX || ba->status == EXPIRED_NONCES) { -+ if (binding->rr) { -+ /* Need to resend home test init to CN */ -+ binding->rr->rr_state |= RR_WAITH; -+ mipv6_send_addr_test_init(&binding->home_addr, -+ &binding->cn_addr, -+ MIPV6_MH_HOTI, -+ binding->rr->hot_cookie); -+ MIPV6_INC_STATS(n_ban_rcvd); -+ } else { -+ DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_NONCE_INDEX" -+ "for non-RR BU"); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ } -+ write_unlock(&bul_lock); -+ return 0; -+ } -+ if (ba->status == EXPIRED_CAREOF_NONCE_INDEX || ba->status == EXPIRED_NONCES) { -+ if (binding->rr) { -+ /* Need to resend care-of test init to CN */ -+ binding->rr->rr_state |= RR_WAITC; -+ mipv6_send_addr_test_init(&binding->coa, -+ &binding->cn_addr, -+ MIPV6_MH_COTI, -+ binding->rr->cot_cookie); -+ MIPV6_INC_STATS(n_ban_rcvd); -+ } else { -+ DEBUG(DBG_WARNING, "Got BA with status EXPIRED_HOME_CAREOF_INDEX" -+ "for non-RR BU"); -+ MIPV6_INC_STATS(n_ba_drop.invalid); -+ } -+ write_unlock(&bul_lock); -+ return 0; -+ } -+ write_unlock(&bul_lock); -+ -+ if (ba->status >= REASON_UNSPECIFIED) { -+ DEBUG(DBG_INFO, "Binding Ack status : %d indicates error", ba->status); -+ mipv6_ba_rcvd(ifindex, src, home, sequence, lifetime, -+ refresh, ba->status); -+ MIPV6_INC_STATS(n_ban_rcvd); -+ return 0; -+ } -+ MIPV6_INC_STATS(n_ba_rcvd); -+ if (mipv6_ba_rcvd(ifindex, src, home, ntohs(ba->sequence), lifetime, -+ refresh, ba->status)) { -+ DEBUG(DBG_WARNING, "mipv6_ba_rcvd failed"); -+ } -+ -+ return 0; -+} -+ -+/** -+ * mipv6_handle_mh_be - Binding Error handler -+ * @cn: source of this packet -+ * @coa: care-of address -+ * @home: home address -+ * @mh: pointer to the beginning of the Mobility Header -+ * -+ **/ -+ -+static int mipv6_handle_mh_be(struct sk_buff *skb, -+ struct in6_addr *home, -+ struct in6_addr *coa, -+ struct in6_addr *cn, -+ struct in6_addr *unused, -+ struct mipv6_mh *mh) -+{ -+ struct mipv6_mh_be *be = (struct mipv6_mh_be *)mh->data; -+ int msg_len = (mh->length+1) << 3; -+ int opt_len; -+ struct in6_addr *hoa; -+ struct bul_inval_args args; -+ -+ DEBUG_FUNC(); -+ -+ if (msg_len > skb->len) -+ return -1; -+ -+ opt_len = msg_len - sizeof(*mh) - sizeof(*be); -+ -+ if (opt_len < 0) { -+ __u32 pos = (__u32)&mh->length - (__u32)skb->nh.raw; -+ icmpv6_send(skb, ICMPV6_PARAMPROB, -+ ICMPV6_HDR_FIELD, pos, skb->dev); -+ -+ DEBUG(DBG_WARNING, "Mobility Header length less than BE"); -+ MIPV6_INC_STATS(n_be_drop.invalid); -+ return -1; -+ } -+ -+ -+ if (!ipv6_addr_any(&be->home_addr)) -+ hoa = &be->home_addr; -+ else -+ hoa = home; -+ -+ MIPV6_INC_STATS(n_be_rcvd); -+ -+ args.all_rr_states = 0; -+ args.cn = cn; -+ args.mn = hoa; -+ -+ switch (be->status) { -+ case 1: /* Home Address Option used without a binding */ -+ /* Get ULP information about CN-MN communication. If -+ nothing in progress, MUST delete. Otherwise MAY -+ ignore. */ -+ args.all_rr_states = 1; -+ case 2: /* Received unknown MH type */ -+ /* If not expecting ack, SHOULD ignore. If MH -+ extension in use, stop it. If not, stop RO for -+ this CN. */ -+ write_lock(&bul_lock); -+ mipv6_bul_iterate(mn_bul_invalidate, &args); -+ write_unlock(&bul_lock); -+ break; -+ } -+ -+ return 0; -+} -+ -+/* -+ * mipv6_bu_rate_limit() : Takes a bulentry, a COA and 'flags' to check -+ * whether BU being sent is for Home Registration or not. -+ * -+ * If the number of BU's sent is fewer than MAX_FAST_UPDATES, this BU -+ * is allowed to be sent at the MAX_UPDATE_RATE. -+ * If the number of BU's sent is greater than or equal to MAX_FAST_UPDATES, -+ * this BU is allowed to be sent at the SLOW_UPDATE_RATE. -+ * -+ * Assumption : This function is not re-entrant. and the caller holds the -+ * bulentry lock (by calling mipv6_bul_get()) to stop races with other -+ * CPU's executing this same function. -+ * -+ * Side-Effects. Either of the following could on success : -+ * 1. Sets consecutive_sends to 1 if the entry is a Home agent -+ * registration or the COA has changed. -+ * 2. Increments consecutive_sends if the number of BU's sent so -+ * far is less than MAX_FAST_UPDATES, and this BU is being sent -+ * atleast MAX_UPDATE_RATE after previous one. -+ * -+ * Return Value : 0 on Success, -1 on Failure -+ */ -+static int mipv6_bu_rate_limit(struct mipv6_bul_entry *bulentry, -+ struct in6_addr *coa, __u8 flags) -+{ -+ if ((flags & MIPV6_BU_F_HOME) || ipv6_addr_cmp(&bulentry->coa, coa)) { -+ /* Home Agent Registration or different COA - restart from 1 */ -+ bulentry->consecutive_sends = 1; -+ return 0; -+ } -+ -+ if (bulentry->consecutive_sends < MAX_FAST_UPDATES) { -+ /* First MAX_FAST_UPDATES can be sent at MAX_UPDATE_RATE */ -+ if (jiffies - bulentry->lastsend < MAX_UPDATE_RATE * HZ) { -+ return -1; -+ } -+ bulentry->consecutive_sends ++; -+ } else { -+ /* Remaining updates SHOULD be sent at SLOW_UPDATE_RATE */ -+ if (jiffies - bulentry->lastsend < SLOW_UPDATE_RATE * HZ) { -+ return -1; -+ } -+ /* Don't inc 'consecutive_sends' to avoid overflow to zero */ -+ } -+ /* OK to send a BU */ -+ return 0; -+} -+ -+/** -+ * mipv6_send_bu - send a Binding Update -+ * @saddr: source address for BU -+ * @daddr: destination address for BU -+ * @coa: care-of address for MN -+ * @initdelay: initial BA wait timeout -+ * @maxackdelay: maximum BA wait timeout -+ * @exp: exponention back off -+ * @flags: flags for BU -+ * @lifetime: granted lifetime for binding -+ * @ops: mobility options -+ * -+ * Send a binding update. 'flags' may contain any of %MIPV6_BU_F_ACK, -+ * %MIPV6_BU_F_HOME, %MIPV6_BU_F_ROUTER bitwise ORed. If -+ * %MIPV6_BU_F_ACK is included retransmission will be attempted until -+ * the update has been acknowledged. Retransmission is done if no -+ * acknowledgement is received within @initdelay seconds. @exp -+ * specifies whether to use exponential backoff (@exp != 0) or linear -+ * backoff (@exp == 0). For exponential backoff the time to wait for -+ * an acknowledgement is doubled on each retransmission until a delay -+ * of @maxackdelay, after which retransmission is no longer attempted. -+ * For linear backoff the delay is kept constant and @maxackdelay -+ * specifies the maximum number of retransmissions instead. If -+ * sub-options are present ops must contain all sub-options to be -+ * added. On a mobile node, use the mobile node's home address for -+ * @saddr. Returns 0 on success, non-zero on failure. -+ * -+ * Caller may not hold @bul_lock. -+ **/ -+int mipv6_send_bu(struct in6_addr *saddr, struct in6_addr *daddr, -+ struct in6_addr *coa, u32 initdelay, -+ u32 maxackdelay, u8 exp, u8 flags, u32 lifetime, -+ struct mipv6_mh_opt *ops) -+{ -+ int ret; -+ __u8 state; -+ __u16 seq = 0; -+ int (*callback)(struct mipv6_bul_entry *); -+ __u32 callback_time; -+ struct mipv6_bul_entry *bulentry; -+ -+ /* First a sanity check: don't send BU to local addresses */ -+ if(ipv6_chk_addr(daddr, NULL)) { -+ DEBUG(DBG_ERROR, "BUG: Trying to send BU to local address"); -+ return -1; -+ } -+ DEBUG(DBG_INFO, "Sending BU to CN %x:%x:%x:%x:%x:%x:%x:%x " -+ "for home address %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(daddr), NIPV6ADDR(saddr)); -+ -+ if ((bulentry = mipv6_bul_get(daddr, saddr)) != NULL) { -+ if (bulentry->state == ACK_ERROR) { -+ /* -+ * Don't send any more BU's to nodes which don't -+ * understanding one. -+ */ -+ DEBUG(DBG_INFO, "Not sending BU to node which doesn't" -+ " understand one"); -+ return -1; -+ } -+ if (mipv6_bu_rate_limit(bulentry, coa, flags) < 0) { -+ DEBUG(DBG_DATADUMP, "Limiting BU sent."); -+ return 0; -+ } -+ } -+ -+ switch (mipv6_rr_state(bulentry, saddr, coa, flags)) { -+ case INPROGRESS_RR: -+ /* We are already doing RR, don't do BU at this time, it is -+ * done automatically later */ -+ DEBUG(DBG_INFO, "RR in progress not sending BU"); -+ return 0; -+ -+ case DO_RR: -+ /* Just do RR and return, BU is done automatically later */ -+ DEBUG(DBG_INFO, "starting RR" ); -+ mipv6_RR_start(saddr, daddr, coa, bulentry, initdelay, -+ maxackdelay, flags, lifetime, ops); -+ return 0; -+ -+ case NO_RR: -+ DEBUG(DBG_DATADUMP, "No RR necessary" ); -+ default: -+ break; -+ } -+ -+ if (bulentry) -+ seq = bulentry->seq + 1; -+ -+ /* Add to binding update list */ -+ -+ if (flags & MIPV6_BU_F_ACK) { -+ DEBUG(DBG_INFO, "Setting bul callback to bul_resend_exp"); -+ /* Send using exponential backoff */ -+ state = RESEND_EXP; -+ callback = bul_resend_exp; -+ callback_time = initdelay; -+ } else { -+ DEBUG(DBG_INFO, "Setting bul callback to bul_entry_expired"); -+ /* No acknowledgement/resending required */ -+ state = ACK_OK; /* pretend we got an ack */ -+ callback = bul_entry_expired; -+ callback_time = lifetime; -+ } -+ -+ /* BU only for the home address */ -+ /* We must hold bul_lock (write) while calling add */ -+ if ((bulentry = mipv6_bul_add(daddr, saddr, coa, lifetime, seq, -+ flags, callback, callback_time, -+ state, initdelay, maxackdelay, ops, -+ NULL)) == NULL) { -+ DEBUG(DBG_INFO, "couldn't update BUL"); -+ return 0; -+ } -+ ret = send_bu_msg(bulentry); -+ -+ return ret; -+} -+ -+int __init mipv6_mh_mn_init(void) -+{ -+ mipv6_mh_register(MIPV6_MH_HOT, mipv6_handle_mh_HC_test); -+ mipv6_mh_register(MIPV6_MH_COT, mipv6_handle_mh_HC_test); -+ mipv6_mh_register(MIPV6_MH_BA, mipv6_handle_mh_ba); -+ mipv6_mh_register(MIPV6_MH_BRR, mipv6_handle_mh_brr); -+ mipv6_mh_register(MIPV6_MH_BE, mipv6_handle_mh_be); -+ -+ return 0; -+} -+ -+void __exit mipv6_mh_mn_exit(void) -+{ -+ mipv6_mh_unregister(MIPV6_MH_HOT); -+ mipv6_mh_unregister(MIPV6_MH_COT); -+ mipv6_mh_unregister(MIPV6_MH_BA); -+ mipv6_mh_unregister(MIPV6_MH_BRR); -+ mipv6_mh_unregister(MIPV6_MH_BE); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/module_cn.c -@@ -0,0 +1,167 @@ -+/* -+ * Mobile IPv6 Common Module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/init.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/mipglue.h> -+ -+#include "bcache.h" -+#include "mipv6_icmp.h" -+#include "stats.h" -+#include "mobhdr.h" -+#include "exthdrs.h" -+ -+int mipv6_debug = 1; -+ -+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 -+MODULE_AUTHOR("MIPL Team"); -+MODULE_DESCRIPTION("Mobile IPv6"); -+MODULE_LICENSE("GPL"); -+MODULE_PARM(mipv6_debug, "i"); -+#endif -+ -+#include "config.h" -+ -+struct mip6_func mip6_fn; -+struct mip6_conf mip6node_cnf = { -+ capabilities: CAP_CN, -+ accept_ret_rout: 1, -+ max_rtr_reachable_time: 0, -+ eager_cell_switching: 0, -+ max_num_tunnels: 0, -+ min_num_tunnels: 0, -+ binding_refresh_advice: 0, -+ bu_lladdr: 0, -+ bu_keymgm: 0, -+ bu_cn_ack: 0 -+}; -+ -+#define MIPV6_BCACHE_SIZE 128 -+ -+/********************************************************************** -+ * -+ * MIPv6 CN Module Init / Cleanup -+ * -+ **********************************************************************/ -+ -+#ifdef CONFIG_SYSCTL -+/* Sysctl table */ -+ctl_table mipv6_mobility_table[] = { -+ {NET_IPV6_MOBILITY_DEBUG, "debuglevel", -+ &mipv6_debug, sizeof(int), 0644, NULL, -+ &proc_dointvec}, -+ {NET_IPV6_MOBILITY_RETROUT, "accept_return_routability", -+ &mip6node_cnf.accept_ret_rout, sizeof(int), 0644, NULL, -+ &proc_dointvec}, -+ {0} -+}; -+ctl_table mipv6_table[] = { -+ {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, -+ {0} -+}; -+ -+static struct ctl_table_header *mipv6_sysctl_header; -+static struct ctl_table mipv6_net_table[]; -+static struct ctl_table mipv6_root_table[]; -+ -+ctl_table mipv6_net_table[] = { -+ {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, -+ {0} -+}; -+ -+ctl_table mipv6_root_table[] = { -+ {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, -+ {0} -+}; -+#endif /* CONFIG_SYSCTL */ -+ -+extern void mipv6_rr_init(void); -+ -+/* Initialize the module */ -+static int __init mip6_init(void) -+{ -+ int err = 0; -+ -+ printk(KERN_INFO "MIPL Mobile IPv6 for Linux Correspondent Node %s (%s)\n", -+ MIPLVERSION, MIPV6VERSION); -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+ printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); -+#endif -+ -+ if ((err = mipv6_bcache_init(MIPV6_BCACHE_SIZE)) < 0) -+ goto bcache_fail; -+ -+ if ((err = mipv6_icmpv6_init()) < 0) -+ goto icmp_fail; -+ -+ if ((err = mipv6_stats_init()) < 0) -+ goto stats_fail; -+ mipv6_rr_init(); -+ -+#ifdef CONFIG_SYSCTL -+ mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); -+#endif -+ -+ if ((err = mipv6_mh_common_init()) < 0) -+ goto mh_fail; -+ -+ MIPV6_SETCALL(mipv6_modify_txoptions, mipv6_modify_txoptions); -+ -+ MIPV6_SETCALL(mipv6_handle_homeaddr, mipv6_handle_homeaddr); -+ MIPV6_SETCALL(mipv6_icmp_swap_addrs, mipv6_icmp_swap_addrs); -+ -+ return 0; -+ -+mh_fail: -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ mipv6_stats_exit(); -+stats_fail: -+ mipv6_icmpv6_exit(); -+icmp_fail: -+ mipv6_bcache_exit(); -+bcache_fail: -+ return err; -+} -+module_init(mip6_init); -+ -+#ifdef MODULE -+/* Cleanup module */ -+static void __exit mip6_exit(void) -+{ -+ printk(KERN_INFO "mip6_base.o exiting.\n"); -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ -+ /* Invalidate all custom kernel hooks. No need to do this -+ separately for all hooks. */ -+ mipv6_invalidate_calls(); -+ -+ mipv6_mh_common_exit(); -+ mipv6_stats_exit(); -+ mipv6_icmpv6_exit(); -+ mipv6_bcache_exit(); -+} -+module_exit(mip6_exit); -+#endif /* MODULE */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/module_ha.c -@@ -0,0 +1,264 @@ -+/* -+ * Mobile IPv6 Home Agent Module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/init.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/mipglue.h> -+#include <net/addrconf.h> -+ -+#include "mobhdr.h" -+#include "tunnel_ha.h" -+#include "ha.h" -+#include "halist.h" -+#include "mipv6_icmp.h" -+//#include "prefix.h" -+#include "bcache.h" -+#include "debug.h" -+ -+int mipv6_use_auth = 0; -+ -+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 -+MODULE_AUTHOR("MIPL Team"); -+MODULE_DESCRIPTION("Mobile IPv6 Home Agent"); -+MODULE_LICENSE("GPL"); -+#endif -+ -+#include "config.h" -+ -+#define MIPV6_HALIST_SIZE 128 -+struct ha_info_opt { -+ u8 type; -+ u8 len; -+ u16 res; -+ u16 pref; -+ u16 ltime; -+}; -+/* -+ * Called from ndisc.c's router_discovery. -+ */ -+static int mipv6_ha_ra_rcv(struct sk_buff *skb, struct ndisc_options *ndopts) -+{ -+ unsigned int ha_info_pref = 0, ha_info_lifetime; -+ int ifi = ((struct inet6_skb_parm *)skb->cb)->iif; -+ struct ra_msg *ra = (struct ra_msg *) skb->h.raw; -+ struct in6_addr *saddr = &skb->nh.ipv6h->saddr; -+ struct in6_addr ll_addr; -+ struct hal { -+ struct in6_addr prefix; -+ int plen; -+ struct hal *next; -+ }; -+ -+ DEBUG_FUNC(); -+ -+ ha_info_lifetime = ntohs(ra->icmph.icmp6_rt_lifetime); -+ ipv6_addr_copy(&ll_addr, saddr); -+ -+ if (ndopts->nd_opts_hai) { -+ struct ha_info_opt *hai = (struct ha_info_opt *)ndopts->nd_opts_hai; -+ ha_info_pref = ntohs(hai->pref); -+ ha_info_lifetime = ntohs(hai->ltime); -+ DEBUG(DBG_DATADUMP, -+ "received home agent info with preference : %d and lifetime : %d", -+ ha_info_pref, ha_info_lifetime); -+ } -+ if (ndopts->nd_opts_pi) { -+ struct nd_opt_hdr *p; -+ for (p = ndopts->nd_opts_pi; -+ p; -+ p = ndisc_next_option(p, ndopts->nd_opts_pi_end)) { -+ struct prefix_info *pinfo; -+ -+ pinfo = (struct prefix_info *) p; -+ -+ if (pinfo->router_address) { -+ DEBUG(DBG_DATADUMP, "Adding router address to " -+ "ha queue \n"); -+ /* If RA has H bit set and Prefix Info -+ * Option R bit set, queue this -+ * address to be added to Home Agents -+ * List. -+ */ -+ if (ipv6_addr_type(&pinfo->prefix) & -+ IPV6_ADDR_LINKLOCAL) -+ continue; -+ if (!ra->icmph.icmp6_home_agent || !ha_info_lifetime) { -+ mipv6_halist_delete(&pinfo->prefix); -+ continue; -+ } else { -+ -+ mipv6_halist_add(ifi, &pinfo->prefix, -+ pinfo->prefix_len, &ll_addr, -+ ha_info_pref, ha_info_lifetime); -+ } -+ -+ } -+ -+ } -+ } -+ return MIPV6_ADD_RTR; -+} -+ -+/********************************************************************** -+ * -+ * MIPv6 Module Init / Cleanup -+ * -+ **********************************************************************/ -+ -+#ifdef CONFIG_SYSCTL -+/* Sysctl table */ -+extern int -+mipv6_max_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+extern int -+mipv6_min_tnls_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+int max_adv = ~(u16)0; -+int min_zero = 0; -+ctl_table mipv6_mobility_table[] = { -+ {NET_IPV6_MOBILITY_BINDING_REFRESH, "binding_refresh_advice", -+ &mip6node_cnf.binding_refresh_advice, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_adv}, -+ -+ {NET_IPV6_MOBILITY_MAX_TNLS, "max_tnls", &mipv6_max_tnls, sizeof(int), -+ 0644, NULL, &mipv6_max_tnls_sysctl}, -+ {NET_IPV6_MOBILITY_MIN_TNLS, "min_tnls", &mipv6_min_tnls, sizeof(int), -+ 0644, NULL, &mipv6_min_tnls_sysctl}, -+ {0} -+}; -+ctl_table mipv6_table[] = { -+ {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, -+ {0} -+}; -+ -+static struct ctl_table_header *mipv6_sysctl_header; -+static struct ctl_table mipv6_net_table[]; -+static struct ctl_table mipv6_root_table[]; -+ -+ctl_table mipv6_net_table[] = { -+ {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, -+ {0} -+}; -+ -+ctl_table mipv6_root_table[] = { -+ {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, -+ {0} -+}; -+#endif /* CONFIG_SYSCTL */ -+ -+extern void mipv6_check_dad(struct in6_addr *haddr); -+extern void mipv6_dad_init(void); -+extern void mipv6_dad_exit(void); -+extern int mipv6_forward(struct sk_buff *); -+ -+/* Initialize the module */ -+static int __init mip6_ha_init(void) -+{ -+ int err = 0; -+ -+ printk(KERN_INFO "MIPL Mobile IPv6 for Linux Home Agent %s (%s)\n", -+ MIPLVERSION, MIPV6VERSION); -+ mip6node_cnf.capabilities = CAP_CN | CAP_HA; -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_rcv_dhaad_req; -+ mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_no_rcv; -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+ printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); -+#endif -+ -+#ifdef CONFIG_SYSCTL -+ mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); -+#endif -+ mipv6_initialize_tunnel(); -+ -+ if ((err = mipv6_ha_init()) < 0) -+ goto ha_fail; -+ -+ MIPV6_SETCALL(mipv6_ra_rcv, mipv6_ha_ra_rcv); -+ MIPV6_SETCALL(mipv6_forward, mipv6_forward); -+ mipv6_dad_init(); -+ MIPV6_SETCALL(mipv6_check_dad, mipv6_check_dad); -+ -+ if ((err = mipv6_halist_init(MIPV6_HALIST_SIZE)) < 0) -+ goto halist_fail; -+ -+// mipv6_initialize_pfx_icmpv6(); -+ -+ return 0; -+ -+halist_fail: -+ mipv6_dad_exit(); -+ mipv6_ha_exit(); -+ha_fail: -+ mipv6_shutdown_tunnel(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ MIPV6_RESETCALL(mipv6_ra_rcv); -+ MIPV6_RESETCALL(mipv6_forward); -+ MIPV6_RESETCALL(mipv6_check_dad); -+ -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ return err; -+} -+module_init(mip6_ha_init); -+ -+#ifdef MODULE -+/* Cleanup module */ -+static void __exit mip6_ha_exit(void) -+{ -+ printk(KERN_INFO "mip6_ha.o exiting.\n"); -+ mip6node_cnf.capabilities &= ~(int)CAP_HA; -+ -+ mipv6_bcache_cleanup(HOME_REGISTRATION); -+ -+ MIPV6_RESETCALL(mipv6_ra_rcv); -+ MIPV6_RESETCALL(mipv6_forward); -+ MIPV6_RESETCALL(mipv6_check_dad); -+ -+ mipv6_halist_exit(); -+// mipv6_shutdown_pfx_icmpv6(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ mipv6_dad_exit(); -+ mipv6_ha_exit(); -+ mipv6_shutdown_tunnel(); -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+} -+module_exit(mip6_ha_exit); -+#endif /* MODULE */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/module_mn.c -@@ -0,0 +1,188 @@ -+/* -+ * Mobile IPv6 Mobile Node Module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Antti Tuominen <ajtuomin@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/module.h> -+#include <linux/init.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/mipglue.h> -+ -+extern int mipv6_debug; -+int mipv6_use_auth = 0; -+ -+#if defined(MODULE) && LINUX_VERSION_CODE > 0x20115 -+MODULE_AUTHOR("MIPL Team"); -+MODULE_DESCRIPTION("Mobile IPv6 Mobile Node"); -+MODULE_LICENSE("GPL"); -+MODULE_PARM(mipv6_debug, "i"); -+#endif -+ -+#include "config.h" -+ -+#include "mobhdr.h" -+#include "mn.h" -+#include "mipv6_icmp.h" -+//#include "prefix.h" -+ -+/* TODO: These will go as soon as we get rid of the last two ioctls */ -+extern int mipv6_ioctl_mn_init(void); -+extern void mipv6_ioctl_mn_exit(void); -+ -+/********************************************************************** -+ * -+ * MIPv6 Module Init / Cleanup -+ * -+ **********************************************************************/ -+ -+#ifdef CONFIG_SYSCTL -+/* Sysctl table */ -+ -+extern int max_rtr_reach_time; -+extern int eager_cell_switching; -+ -+static int max_reach = 1000; -+static int min_reach = 1; -+static int max_one = 1; -+static int min_zero = 0; -+ -+extern int -+mipv6_mdetect_mech_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+extern int -+mipv6_router_reach_sysctl(ctl_table *, int, struct file *, void *, size_t *); -+ -+ctl_table mipv6_mobility_table[] = { -+ {NET_IPV6_MOBILITY_BU_F_LLADDR, "bu_flag_lladdr", -+ &mip6node_cnf.bu_lladdr, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ {NET_IPV6_MOBILITY_BU_F_KEYMGM, "bu_flag_keymgm", -+ &mip6node_cnf.bu_keymgm, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ {NET_IPV6_MOBILITY_BU_F_CN_ACK, "bu_flag_cn_ack", -+ &mip6node_cnf.bu_cn_ack, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ -+ {NET_IPV6_MOBILITY_ROUTER_REACH, "max_router_reachable_time", -+ &max_rtr_reach_time, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_reach, &max_reach}, -+ -+ {NET_IPV6_MOBILITY_MDETECT_MECHANISM, "eager_cell_switching", -+ &eager_cell_switching, sizeof(int), 0644, NULL, -+ &proc_dointvec_minmax, &sysctl_intvec, 0, &min_zero, &max_one}, -+ -+ {0} -+}; -+ctl_table mipv6_table[] = { -+ {NET_IPV6_MOBILITY, "mobility", NULL, 0, 0555, mipv6_mobility_table}, -+ {0} -+}; -+ -+static struct ctl_table_header *mipv6_sysctl_header; -+static struct ctl_table mipv6_net_table[]; -+static struct ctl_table mipv6_root_table[]; -+ -+ctl_table mipv6_net_table[] = { -+ {NET_IPV6, "ipv6", NULL, 0, 0555, mipv6_table}, -+ {0} -+}; -+ -+ctl_table mipv6_root_table[] = { -+ {CTL_NET, "net", NULL, 0, 0555, mipv6_net_table}, -+ {0} -+}; -+#endif /* CONFIG_SYSCTL */ -+ -+/* Initialize the module */ -+static int __init mip6_mn_init(void) -+{ -+ int err = 0; -+ -+ printk(KERN_INFO "MIPL Mobile IPv6 for Linux Mobile Node %s (%s)\n", -+ MIPLVERSION, MIPV6VERSION); -+ mip6node_cnf.capabilities = CAP_CN | CAP_MN; -+ -+#ifdef CONFIG_IPV6_MOBILITY_DEBUG -+ printk(KERN_INFO "Debug-level: %d\n", mipv6_debug); -+#endif -+ -+#ifdef CONFIG_SYSCTL -+ mipv6_sysctl_header = register_sysctl_table(mipv6_root_table, 0); -+#endif -+ if ((err = mipv6_mn_init()) < 0) -+ goto mn_fail; -+ -+ mipv6_mh_mn_init(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = mipv6_icmpv6_rcv_dhaad_rep; -+ mip6_fn.icmpv6_dhaad_req_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_pfxadv_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_pfxsol_rcv = mipv6_icmpv6_no_rcv; -+ mip6_fn.icmpv6_paramprob_rcv = mipv6_icmpv6_rcv_paramprob; -+ -+// mipv6_initialize_pfx_icmpv6(); -+ -+ if ((err = mipv6_ioctl_mn_init()) < 0) -+ goto ioctl_fail; -+ -+ return 0; -+ -+ioctl_fail: -+// mipv6_shutdown_pfx_icmpv6(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ mipv6_mh_mn_exit(); -+ mipv6_mn_exit(); -+mn_fail: -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+ return err; -+} -+module_init(mip6_mn_init); -+ -+#ifdef MODULE -+/* Cleanup module */ -+static void __exit mip6_mn_exit(void) -+{ -+ printk(KERN_INFO "mip6_mn.o exiting.\n"); -+ mip6node_cnf.capabilities &= ~(int)CAP_MN; -+ -+ mipv6_ioctl_mn_exit(); -+// mipv6_shutdown_pfx_icmpv6(); -+ -+ mip6_fn.icmpv6_dhaad_rep_rcv = NULL; -+ mip6_fn.icmpv6_dhaad_req_rcv = NULL; -+ mip6_fn.icmpv6_pfxadv_rcv = NULL; -+ mip6_fn.icmpv6_pfxsol_rcv = NULL; -+ mip6_fn.icmpv6_paramprob_rcv = NULL; -+ -+ mipv6_mn_exit(); -+ -+/* common cleanup */ -+#ifdef CONFIG_SYSCTL -+ unregister_sysctl_table(mipv6_sysctl_header); -+#endif -+} -+module_exit(mip6_mn_exit); -+#endif /* MODULE */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/multiaccess_ctl.c -@@ -0,0 +1,287 @@ -+/* -+ * 2001 (c) Oy L M Ericsson Ab -+ * -+ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com> -+ * -+ * $Id$ -+ * -+ */ -+ -+/* -+ * Vertical hand-off information manager -+ */ -+ -+#include <linux/netdevice.h> -+#include <linux/in6.h> -+#include <linux/module.h> -+#include <linux/init.h> -+#include <linux/proc_fs.h> -+#include <linux/string.h> -+#include <linux/kernel.h> -+#include <asm/io.h> -+#include <asm/uaccess.h> -+#include <linux/list.h> -+#include "multiaccess_ctl.h" -+#include "debug.h" -+ -+/* -+ * Local variables -+ */ -+static LIST_HEAD(if_list); -+ -+/* Internal interface information list */ -+struct ma_if_info { -+ struct list_head list; -+ int interface_id; -+ int preference; -+ __u8 status; -+}; -+ -+/** -+ * ma_ctl_get_preference - get preference value for interface -+ * @ifi: interface index -+ * -+ * Returns integer value preference for given interface. -+ **/ -+int ma_ctl_get_preference(int ifi) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ int pref = 0; -+ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == ifi) { -+ pref = info->preference; -+ return pref; -+ } -+ } -+ return -1; -+} -+/** -+ * ma_ctl_get_preference - get preference value for interface -+ * @ifi: interface index -+ * -+ * Returns integer value interface index for interface with highest preference. -+ **/ -+int ma_ctl_get_preferred_if(void) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info, *pref_if = NULL; -+ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (!pref_if || (info->preference > pref_if->preference)) { -+ pref_if = info; -+ } -+ } -+ if (pref_if) return pref_if->interface_id; -+ return 0; -+} -+/** -+ * ma_ctl_set_preference - set preference for interface -+ * @arg: ioctl args -+ * -+ * Sets preference of an existing interface (called by ioctl). -+ **/ -+void ma_ctl_set_preference(unsigned long arg) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ struct ma_if_uinfo uinfo; -+ -+ memset(&uinfo, 0, sizeof(struct ma_if_uinfo)); -+ if (copy_from_user(&uinfo, (struct ma_if_uinfo *)arg, -+ sizeof(struct ma_if_uinfo)) < 0) { -+ DEBUG(DBG_WARNING, "copy_from_user failed"); -+ return; -+ } -+ -+ /* check if the interface exists */ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == uinfo.interface_id) { -+ info->preference = uinfo.preference; -+ return; -+ } -+ } -+} -+ -+/** -+ * ma_ctl_add_iface - add new interface to list -+ * @if_index: interface index -+ * -+ * Adds new interface entry to preference list. Preference is set to -+ * the same value as @if_index. Entry @status is set to -+ * %MA_IFACE_NOT_USED. -+ **/ -+void ma_ctl_add_iface(int if_index) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ -+ DEBUG_FUNC(); -+ -+ /* check if the interface already exists */ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == if_index) { -+ info->status = MA_IFACE_NOT_USED; -+ info->preference = if_index; -+ return; -+ } -+ } -+ -+ info = kmalloc(sizeof(struct ma_if_info), GFP_ATOMIC); -+ if (info == NULL) { -+ DEBUG(DBG_ERROR, "Out of memory"); -+ return; -+ } -+ memset(info, 0, sizeof(struct ma_if_info)); -+ info->interface_id = if_index; -+ info->preference = if_index; -+ info->status = MA_IFACE_NOT_USED; -+ list_add(&info->list, &if_list); -+} -+ -+/** -+ * ma_ctl_del_iface - remove entry from the list -+ * @if_index: interface index -+ * -+ * Removes entry for interface @if_index from preference list. -+ **/ -+int ma_ctl_del_iface(int if_index) -+{ -+ struct list_head *lh, *next; -+ struct ma_if_info *info; -+ -+ DEBUG_FUNC(); -+ -+ /* if the iface exists, change availability to 0 */ -+ list_for_each_safe(lh, next, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (info->interface_id == if_index) { -+ list_del(&info->list); -+ kfree(info); -+ return 0; -+ } -+ } -+ -+ return -1; -+} -+ -+/** -+ * ma_ctl_upd_iface - update entry (and list) -+ * @if_index: interface to update -+ * @status: new status for interface -+ * @change_if_index: new interface -+ * -+ * Updates @if_index entry on preference list. Entry status is set to -+ * @status. If new @status is %MA_IFACE_CURRENT, updates list to have -+ * only one current device. If @status is %MA_IFACE_NOT_PRESENT, -+ * entry is deleted and further if entry had %MA_IFACE_CURRENT set, -+ * new current device is looked up and returned in @change_if_index. -+ * New preferred interface is also returned if current device changes -+ * to %MA_IFACE_NOT_USED. Returns 0 on success, otherwise negative. -+ **/ -+int ma_ctl_upd_iface(int if_index, int status, int *change_if_index) -+{ -+ struct list_head *lh, *tmp; -+ struct ma_if_info *info, *pref = NULL; -+ int found = 0; -+ -+ DEBUG_FUNC(); -+ -+ *change_if_index = 0; -+ -+ /* check if the interface exists */ -+ list_for_each_safe(lh, tmp, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (status == MA_IFACE_NOT_PRESENT) { -+ if (info->interface_id == if_index) { -+ list_del_init(&info->list); -+ kfree(info); -+ found = 1; -+ break; -+ } -+ } else if (status == MA_IFACE_CURRENT) { -+ if (info->interface_id == if_index) { -+ info->status |= MA_IFACE_CURRENT; -+ found = 1; -+ } else { -+ info->status |= MA_IFACE_NOT_USED; -+ } -+ } else if (status == MA_IFACE_NOT_USED) { -+ if (info->interface_id == if_index) { -+ if (info->status | MA_IFACE_CURRENT) { -+ found = 1; -+ } -+ info->status &= !MA_IFACE_CURRENT; -+ info->status |= MA_IFACE_NOT_USED; -+ info->status &= !MA_IFACE_HAS_ROUTER; -+ } -+ break; -+ } else if (status == MA_IFACE_HAS_ROUTER) { -+ if (info->interface_id == if_index) { -+ info->status |= MA_IFACE_HAS_ROUTER; -+ } -+ return 0; -+ } -+ } -+ -+ if (status & (MA_IFACE_NOT_USED|MA_IFACE_NOT_PRESENT) && found) { -+ /* select new interface */ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ if (pref == NULL || ((info->preference > pref->preference) && -+ info->status & MA_IFACE_HAS_ROUTER)) -+ pref = info; -+ } -+ if (pref) { -+ *change_if_index = pref->interface_id; -+ pref->status |= MA_IFACE_CURRENT; -+ } else { -+ *change_if_index = -1; -+ } -+ return 0; -+ } -+ -+ if (found) return 0; -+ -+ return -1; -+} -+ -+static int if_proc_info(char *buffer, char **start, off_t offset, -+ int length) -+{ -+ struct list_head *lh; -+ struct ma_if_info *info; -+ int len = 0; -+ -+ list_for_each(lh, &if_list) { -+ info = list_entry(lh, struct ma_if_info, list); -+ len += sprintf(buffer + len, "%02d %010d %1d %1d\n", -+ info->interface_id, info->preference, -+ !!(info->status & MA_IFACE_HAS_ROUTER), -+ !!(info->status & MA_IFACE_CURRENT)); -+ } -+ -+ *start = buffer + offset; -+ -+ len -= offset; -+ -+ if (len > length) len = length; -+ -+ return len; -+ -+} -+ -+void ma_ctl_init(void) -+{ -+ proc_net_create("mip6_iface", 0, if_proc_info); -+} -+ -+void ma_ctl_clean(void) -+{ -+ proc_net_remove("mip6_iface"); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/multiaccess_ctl.h -@@ -0,0 +1,77 @@ -+/* -+ * 2001 (c) Oy L M Ericsson Ab -+ * -+ * Author: NomadicLab / Ericsson Research <ipv6@nomadiclab.com> -+ * -+ * $Id$ -+ * -+ */ -+ -+#ifndef _MULTIACCESS_CTL_H -+#define _MULTIACCESS_CTL_H -+ -+/* status */ -+#define MA_IFACE_NOT_PRESENT 0x01 -+#define MA_IFACE_NOT_USED 0x02 -+#define MA_IFACE_HAS_ROUTER 0x04 -+#define MA_IFACE_CURRENT 0x10 -+ -+struct ma_if_uinfo { -+ int interface_id; -+ int preference; -+ __u8 status; -+}; -+/* -+ * @ma_ctl_get_preferred_id: returns most preferred interface id -+ */ -+int ma_ctl_get_preferred_if(void); -+ -+/* @ma_ctl_get_preference: returns preference for an interface -+ * @name: name of the interface (dev->name) -+ */ -+int ma_ctl_get_preference(int ifi); -+ -+/* -+ * Public function: ma_ctl_set_preference -+ * Description: Set preference of an existing interface (called by ioctl) -+ * Returns: -+ */ -+void ma_ctl_set_preference(unsigned long); -+ -+/* -+ * Public function: ma_ctl_add_iface -+ * Description: Inform control module to insert a new interface -+ * Returns: 0 if success, any other number means an error -+ */ -+void ma_ctl_add_iface(int); -+ -+/* -+ * Public function: ma_ctl_del_iface -+ * Description: Inform control module to remove an obsolete interface -+ * Returns: 0 if success, any other number means an error -+ */ -+int ma_ctl_del_iface(int); -+ -+/* -+ * Public function: ma_ctl_upd_iface -+ * Description: Inform control module of status change. -+ * Returns: 0 if success, any other number means an error -+ */ -+int ma_ctl_upd_iface(int, int, int *); -+ -+/* -+ * Public function: ma_ctl_init -+ * Description: XXX -+ * Returns: XXX -+ */ -+void ma_ctl_init(void); -+ -+/* -+ * Public function: ma_ctl_clean -+ * Description: XXX -+ * Returns: - -+ */ -+void ma_ctl_clean(void); -+ -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/ndisc_ha.c -@@ -0,0 +1,596 @@ -+/* -+ * Mobile IPv6 Duplicate Address Detection Functions -+ * -+ * Authors: -+ * Krishna Kumar <krkumar@us.ibm.com> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/autoconf.h> -+#include <linux/types.h> -+#include <linux/init.h> -+#include <linux/skbuff.h> -+#include <linux/in6.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/mipv6.h> -+ -+#include "debug.h" -+#include "bcache.h" -+#include "ha.h" /* mipv6_generate_ll_addr */ -+ -+/* -+ * Binding Updates from MN are cached in this structure till DAD is performed. -+ * This structure is used to retrieve a pending Binding Update for the HA to -+ * reply to after performing DAD. The first cell is different from the rest as -+ * follows : -+ * 1. The first cell is used to chain the remaining cells. -+ * 2. The timeout of the first cell is used to delete expired entries -+ * in the list of cells, while the timeout of the other cells are -+ * used for timing out a NS request so as to reply to a BU. -+ * 3. The only elements of the first cell that are used are : -+ * next, prev, and callback_timer. -+ * -+ * TODO : Don't we need to do pneigh_lookup on the Link Local address ? -+ */ -+struct mipv6_dad_cell { -+ /* Information needed for DAD management */ -+ struct mipv6_dad_cell *next; /* Next element on the DAD list */ -+ struct mipv6_dad_cell *prev; /* Prev element on the DAD list */ -+ __u16 probes; /* Number of times to probe for addr */ -+ __u16 flags; /* Entry flags - see below */ -+ struct timer_list callback_timer; /* timeout for entry */ -+ -+ /* Information needed for performing DAD */ -+ struct inet6_ifaddr *ifp; -+ int ifindex; -+ struct in6_addr daddr; -+ struct in6_addr haddr; /* home address */ -+ struct in6_addr ll_haddr; /* Link Local value of haddr */ -+ struct in6_addr coa; -+ struct in6_addr rep_coa; -+ __u32 ba_lifetime; -+ __u16 sequence; -+ __u8 bu_flags; -+}; -+ -+/* Values for the 'flags' field in the mipv6_dad_cell */ -+#define DAD_INIT_ENTRY 0 -+#define DAD_DUPLICATE_ADDRESS 1 -+#define DAD_UNIQUE_ADDRESS 2 -+ -+/* Head of the pending DAD list */ -+static struct mipv6_dad_cell dad_cell_head; -+ -+/* Lock to access the pending DAD list */ -+static rwlock_t dad_lock = RW_LOCK_UNLOCKED; -+ -+/* Timer routine which deletes 'expired' entries in the DAD list */ -+static void mipv6_dad_delete_old_entries(unsigned long unused) -+{ -+ struct mipv6_dad_cell *curr, *next; -+ unsigned long next_time = 0; -+ -+ write_lock(&dad_lock); -+ curr = dad_cell_head.next; -+ while (curr != &dad_cell_head) { -+ next = curr->next; -+ if (curr->flags != DAD_INIT_ENTRY) { -+ if (curr->callback_timer.expires <= jiffies) { -+ /* Entry has expired, free it up. */ -+ curr->next->prev = curr->prev; -+ curr->prev->next = curr->next; -+ in6_ifa_put(curr->ifp); -+ kfree(curr); -+ } else if (next_time < -+ curr->callback_timer.expires) { -+ next_time = curr->callback_timer.expires; -+ } -+ } -+ curr = next; -+ } -+ write_unlock(&dad_lock); -+ if (next_time) { -+ /* -+ * Start another timer if more cells need to be removed at -+ * a later stage. -+ */ -+ dad_cell_head.callback_timer.expires = next_time; -+ add_timer(&dad_cell_head.callback_timer); -+ } -+} -+ -+/* -+ * Queue a timeout routine to clean up 'expired' DAD entries. -+ */ -+static void mipv6_start_dad_head_timer(struct mipv6_dad_cell *cell) -+{ -+ unsigned long expire = jiffies + -+ cell->ifp->idev->nd_parms->retrans_time * 10; -+ -+ if (!timer_pending(&dad_cell_head.callback_timer) || -+ expire < dad_cell_head.callback_timer.expires) { -+ /* -+ * Add timer if none pending, or mod the timer if new -+ * cell needs to be expired before existing timer runs. -+ * -+ * We let the cell remain as long as possible, so that -+ * new BU's as part of retransmissions don't have to go -+ * through DAD before replying. -+ */ -+ dad_cell_head.callback_timer.expires = expire; -+ -+ /* -+ * Keep the cell around for atleast some time to handle -+ * retransmissions or BU's due to fast MN movement. This -+ * is needed otherwise a previous timeout can delete all -+ * expired entries including this new one. -+ */ -+ cell->callback_timer.expires = jiffies + -+ cell->ifp->idev->nd_parms->retrans_time * 5; -+ if (!timer_pending(&dad_cell_head.callback_timer)) { -+ add_timer(&dad_cell_head.callback_timer); -+ } else { -+ mod_timer(&dad_cell_head.callback_timer, expire); -+ } -+ } -+} -+ -+ -+/* Join solicited node MC address */ -+static inline void mipv6_join_sol_mc_addr(struct in6_addr *addr, -+ struct net_device *dev) -+{ -+ struct in6_addr maddr; -+ -+ /* Join solicited node MC address */ -+ addrconf_addr_solict_mult(addr, &maddr); -+ ipv6_dev_mc_inc(dev, &maddr); -+} -+ -+/* Leave solicited node MC address */ -+static inline void mipv6_leave_sol_mc_addr(struct in6_addr *addr, -+ struct net_device *dev) -+{ -+ struct in6_addr maddr; -+ -+ addrconf_addr_solict_mult(addr, &maddr); -+ ipv6_dev_mc_dec(dev, &maddr); -+} -+ -+/* Send a NS */ -+static inline void mipv6_dad_send_ns(struct inet6_ifaddr *ifp, -+ struct in6_addr *haddr) -+{ -+ struct in6_addr unspec; -+ struct in6_addr mcaddr; -+ -+ ipv6_addr_set(&unspec, 0, 0, 0, 0); -+ addrconf_addr_solict_mult(haddr, &mcaddr); -+ -+ /* addr is 'unspec' since we treat this address as transient */ -+ ndisc_send_ns(ifp->idev->dev, NULL, haddr, &mcaddr, &unspec); -+} -+ -+/* -+ * Search for a home address in the list of pending DAD's. Called from -+ * Neighbor Advertisement -+ * Return values : -+ * -1 : No DAD entry found for this advertisement, or entry already -+ * finished processing. -+ * 0 : Entry found waiting for DAD to finish. -+ */ -+static int dad_search_haddr(struct in6_addr *ll_haddr, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u16 * seq, struct inet6_ifaddr **ifp) -+{ -+ struct mipv6_dad_cell *cell; -+ -+ read_lock(&dad_lock); -+ cell = dad_cell_head.next; -+ while (cell != &dad_cell_head && -+ ipv6_addr_cmp(&cell->ll_haddr, ll_haddr) && -+ ipv6_addr_cmp(&cell->haddr, ll_haddr)) { -+ cell = cell->next; -+ } -+ if (cell == &dad_cell_head || cell->flags != DAD_INIT_ENTRY) { -+ /* Not found element, or element already finished processing */ -+ if (cell != &dad_cell_head) { -+ /* -+ * Set the state to DUPLICATE, even if it was UNIQUE -+ * earlier. It is not needed to setup timer via -+ * mipv6_start_dad_head_timer since this must have -+ * already been done. -+ */ -+ cell->flags = DAD_DUPLICATE_ADDRESS; -+ } -+ read_unlock(&dad_lock); -+ return -1; -+ } -+ -+ /* -+ * The NA found an unprocessed entry in the DAD list. Expire this -+ * entry since another node advertised this address. Caller should -+ * reject BU (DAD failed). -+ */ -+ ipv6_addr_copy(daddr, &cell->daddr); -+ ipv6_addr_copy(haddr, &cell->haddr); -+ ipv6_addr_copy(coa, &cell->coa); -+ ipv6_addr_copy(rep_coa, &cell->rep_coa); -+ *seq = cell->sequence; -+ *ifp = cell->ifp; -+ -+ if (del_timer(&cell->callback_timer) == 0) { -+ /* Timer already deleted, race with Timeout Handler */ -+ /* No action needed */ -+ } -+ -+ cell->flags = DAD_DUPLICATE_ADDRESS; -+ -+ /* Now leave this address to avoid future processing of NA's */ -+ mipv6_leave_sol_mc_addr(&cell->ll_haddr, cell->ifp->idev->dev); -+ /* Leave also global address, if link local address was in use */ -+ if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) -+ mipv6_leave_sol_mc_addr(&cell->haddr, cell->ifp->idev->dev); -+ /* Start dad_head timer to remove this entry */ -+ mipv6_start_dad_head_timer(cell); -+ -+ read_unlock(&dad_lock); -+ -+ return 0; -+} -+ -+/* ENTRY routine called via Neighbor Advertisement */ -+void mipv6_check_dad(struct in6_addr *ll_haddr) -+{ -+ struct in6_addr daddr, haddr, coa, rep_coa; -+ struct inet6_ifaddr *ifp; -+ __u16 seq; -+ -+ if (dad_search_haddr(ll_haddr, &daddr, &haddr, &coa, &rep_coa, &seq, -+ &ifp) < 0) { -+ /* -+ * Didn't find entry, or no action needed (the action has -+ * already been performed). -+ */ -+ return; -+ } -+ -+ /* -+ * A DAD cell was present, meaning that there is a pending BU -+ * request for 'haddr' - reject the BU. -+ */ -+ mipv6_bu_finish(ifp, 0, DUPLICATE_ADDR_DETECT_FAIL, -+ &daddr, &haddr, &coa, &rep_coa, 0, seq, 0, NULL); -+ return; -+} -+ -+/* -+ * Check if the passed 'cell' is in the list of pending DAD's. Called from -+ * the Timeout Handler. -+ * -+ * Assumes that the caller is holding the dad_lock in reader mode. -+ */ -+static int dad_search_cell(struct mipv6_dad_cell *cell) -+{ -+ struct mipv6_dad_cell *tmp; -+ -+ tmp = dad_cell_head.next; -+ while (tmp != &dad_cell_head && tmp != cell) { -+ tmp = tmp->next; -+ } -+ if (tmp == cell) { -+ if (cell->flags == DAD_INIT_ENTRY) { -+ /* Found valid entry */ -+ if (--cell->probes == 0) { -+ /* -+ * Retransmission's are over - return success. -+ */ -+ cell->flags = DAD_UNIQUE_ADDRESS; -+ -+ /* -+ * Leave this address to avoid future -+ * processing of NA's. -+ */ -+ mipv6_leave_sol_mc_addr(&cell->ll_haddr, -+ cell->ifp->idev-> -+ dev); -+ if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) -+ mipv6_leave_sol_mc_addr(&cell->haddr, -+ cell->ifp->idev->dev); -+ /* start timeout to delete this cell. */ -+ mipv6_start_dad_head_timer(cell); -+ return 0; -+ } -+ /* -+ * Retransmission not finished, send another NS and -+ * return failure. -+ */ -+ mipv6_dad_send_ns(cell->ifp, &cell->ll_haddr); -+ if (ipv6_addr_cmp(&cell->ll_haddr, &cell->haddr)) -+ mipv6_leave_sol_mc_addr(&cell->haddr, -+ cell->ifp->idev->dev); -+ cell->callback_timer.expires = jiffies + -+ cell->ifp->idev->nd_parms->retrans_time; -+ add_timer(&cell->callback_timer); -+ } else { -+ /* -+ * This means that an NA was received before the -+ * timeout and when the state changed from -+ * DAD_INIT_ENTRY, the BU got failed as a result. -+ * There is nothing to be done. -+ */ -+ } -+ } -+ return -1; -+} -+ -+/* ENTRY routine called via Timeout */ -+static void mipv6_dad_timeout(unsigned long arg) -+{ -+ __u8 ba_status = SUCCESS; -+ struct in6_addr daddr; -+ struct in6_addr haddr; -+ struct in6_addr coa; -+ struct in6_addr rep_coa; -+ struct inet6_ifaddr *ifp; -+ int ifindex; -+ __u32 ba_lifetime; -+ __u16 sequence; -+ __u8 flags; -+ struct mipv6_dad_cell *cell = (struct mipv6_dad_cell *) arg; -+ -+ /* -+ * If entry is not in the list, we have already sent BU Failure -+ * after getting a NA. -+ */ -+ read_lock(&dad_lock); -+ if (dad_search_cell(cell) < 0) { -+ /* -+ * 'cell' is no longer valid (may not be in the list or -+ * is already processed, due to NA processing), or NS -+ * retransmissions are not yet over. -+ */ -+ read_unlock(&dad_lock); -+ return; -+ } -+ -+ /* This is the final Timeout. Send Bind Ack Success */ -+ -+ ifp = cell->ifp; -+ ifindex = cell->ifindex; -+ ba_lifetime = cell->ba_lifetime; -+ sequence = cell->sequence; -+ flags = cell->bu_flags; -+ -+ ipv6_addr_copy(&daddr, &cell->daddr); -+ ipv6_addr_copy(&haddr, &cell->haddr); -+ ipv6_addr_copy(&coa, &cell->coa); -+ ipv6_addr_copy(&rep_coa, &cell->rep_coa); -+ read_unlock(&dad_lock); -+ -+ /* Send BU Acknowledgement Success */ -+ mipv6_bu_finish(ifp, ifindex, ba_status, -+ &daddr, &haddr, &coa, &rep_coa, -+ ba_lifetime, sequence, flags, NULL); -+ return; -+} -+ -+/* -+ * Check if original home address exists in our DAD pending list, if so return -+ * the cell. -+ * -+ * Assumes that the caller is holding the dad_lock in writer mode. -+ */ -+static struct mipv6_dad_cell *mipv6_dad_get_cell(struct in6_addr *haddr) -+{ -+ struct mipv6_dad_cell *cell; -+ -+ cell = dad_cell_head.next; -+ while (cell != &dad_cell_head -+ && ipv6_addr_cmp(&cell->haddr, haddr)) { -+ cell = cell->next; -+ } -+ if (cell == &dad_cell_head) { -+ /* Not found element */ -+ return NULL; -+ } -+ return cell; -+} -+ -+/* -+ * Save all parameters needed for doing a Bind Ack in the mipv6_dad_cell -+ * structure. -+ */ -+static void mipv6_dad_save_cell(struct mipv6_dad_cell *cell, -+ struct inet6_ifaddr *ifp, int ifindex, -+ struct in6_addr *daddr, -+ struct in6_addr *haddr, -+ struct in6_addr *coa, -+ struct in6_addr *rep_coa, -+ __u32 ba_lifetime, -+ __u16 sequence, __u8 flags) -+{ -+ in6_ifa_hold(ifp); -+ cell->ifp = ifp; -+ cell->ifindex = ifindex; -+ -+ ipv6_addr_copy(&cell->daddr, daddr); -+ ipv6_addr_copy(&cell->haddr, haddr); -+ ipv6_addr_copy(&cell->coa, coa); -+ ipv6_addr_copy(&cell->rep_coa, rep_coa); -+ -+ /* Convert cell->ll_haddr to Link Local address */ -+ if (flags & MIPV6_BU_F_LLADDR) -+ mipv6_generate_ll_addr(&cell->ll_haddr, haddr); -+ else -+ ipv6_addr_copy(&cell->ll_haddr, haddr); -+ -+ cell->ba_lifetime = ba_lifetime; -+ cell->sequence = sequence; -+ cell->bu_flags = flags; -+} -+ -+/* -+ * Top level DAD routine for performing DAD. -+ * -+ * Return values -+ * 0 : Don't need to do DAD. -+ * 1 : Need to do DAD. -+ * -n : Error, where 'n' is the reason for the error. -+ * -+ * Assumption : DAD process has been optimized by using cached values upto -+ * some time. However sometimes this can cause problems. Eg. when the first -+ * BU was received, DAD might have failed. Before the second BU arrived, -+ * the node using MN's home address might have stopped using it, but still -+ * we will return DAD_DUPLICATE_ADDRESS based on the first DAD's result. Or -+ * this can go the other way around. However, it is a very small possibility -+ * and thus optimization is turned on by default. It is possible to change -+ * this feature (needs a little code-rewriting in this routine), but -+ * currently DAD result is being cached for performance reasons. -+ */ -+int mipv6_dad_start(struct inet6_ifaddr *ifp, int ifindex, -+ struct in6_addr *daddr, struct in6_addr *haddr, -+ struct in6_addr *coa, struct in6_addr *rep_coa, -+ __u32 ba_lifetime, __u16 sequence, __u8 flags) -+{ -+ int found; -+ struct mipv6_dad_cell *cell; -+ struct mipv6_bce bc_entry; -+ -+ if (ifp->idev->cnf.dad_transmits == 0) { -+ /* DAD is not configured on the HA, return SUCCESS */ -+ return 0; -+ } -+ -+ if (mipv6_bcache_get(haddr, daddr, &bc_entry) == 0) { -+ /* -+ * We already have an entry in our cache - don't need to -+ * do DAD as we are already defending this home address. -+ */ -+ return 0; -+ } -+ -+ write_lock(&dad_lock); -+ if ((cell = mipv6_dad_get_cell(haddr)) != NULL) { -+ /* -+ * An existing entry for BU was found in our cache due -+ * to retransmission of the BU or a new COA registration. -+ */ -+ switch (cell->flags) { -+ case DAD_INIT_ENTRY: -+ /* Old entry is waiting for DAD to complete */ -+ break; -+ case DAD_UNIQUE_ADDRESS: -+ /* DAD is finished successfully - return success. */ -+ write_unlock(&dad_lock); -+ return 0; -+ case DAD_DUPLICATE_ADDRESS: -+ /* -+ * DAD is finished and we got a NA while doing BU - -+ * return failure. -+ */ -+ write_unlock(&dad_lock); -+ return -DUPLICATE_ADDR_DETECT_FAIL; -+ default: -+ /* Unknown state - should never happen */ -+ DEBUG(DBG_WARNING, -+ "cell entry in unknown state : %d", -+ cell->flags); -+ write_unlock(&dad_lock); -+ return -REASON_UNSPECIFIED; -+ } -+ found = 1; -+ } else { -+ if ((cell = (struct mipv6_dad_cell *) -+ kmalloc(sizeof(struct mipv6_dad_cell), GFP_ATOMIC)) -+ == NULL) { -+ return -INSUFFICIENT_RESOURCES; -+ } -+ found = 0; -+ } -+ -+ mipv6_dad_save_cell(cell, ifp, ifindex, daddr, haddr, coa, rep_coa, -+ ba_lifetime, sequence, flags); -+ -+ if (!found) { -+ cell->flags = DAD_INIT_ENTRY; -+ cell->probes = ifp->idev->cnf.dad_transmits; -+ -+ /* Insert element on dad_cell_head list */ -+ dad_cell_head.prev->next = cell; -+ cell->next = &dad_cell_head; -+ cell->prev = dad_cell_head.prev; -+ dad_cell_head.prev = cell; -+ write_unlock(&dad_lock); -+ if (flags & MIPV6_BU_F_LLADDR) { -+ /* join the solicited node MC of the global homeaddr.*/ -+ mipv6_join_sol_mc_addr(&cell->haddr, ifp->idev->dev); -+ /* Send a NS */ -+ mipv6_dad_send_ns(ifp, &cell->haddr); -+ } -+ /* join the solicited node MC of the homeaddr. */ -+ mipv6_join_sol_mc_addr(&cell->ll_haddr, ifp->idev->dev); -+ -+ /* Send a NS */ -+ mipv6_dad_send_ns(ifp, &cell->ll_haddr); -+ -+ /* Initialize timer for this cell to timeout the NS. */ -+ init_timer(&cell->callback_timer); -+ cell->callback_timer.data = (unsigned long) cell; -+ cell->callback_timer.function = mipv6_dad_timeout; -+ cell->callback_timer.expires = jiffies + -+ ifp->idev->nd_parms->retrans_time; -+ add_timer(&cell->callback_timer); -+ } else { -+ write_unlock(&dad_lock); -+ } -+ return 1; -+} -+ -+void __init mipv6_dad_init(void) -+{ -+ dad_cell_head.next = dad_cell_head.prev = &dad_cell_head; -+ init_timer(&dad_cell_head.callback_timer); -+ dad_cell_head.callback_timer.data = 0; -+ dad_cell_head.callback_timer.function = -+ mipv6_dad_delete_old_entries; -+} -+ -+void __exit mipv6_dad_exit(void) -+{ -+ struct mipv6_dad_cell *curr, *next; -+ -+ write_lock_bh(&dad_lock); -+ del_timer(&dad_cell_head.callback_timer); -+ -+ curr = dad_cell_head.next; -+ while (curr != &dad_cell_head) { -+ next = curr->next; -+ del_timer(&curr->callback_timer); -+ if (curr->flags == DAD_INIT_ENTRY) { -+ /* -+ * We were in DAD_INIT state and listening to the -+ * solicited node MC address - need to stop that. -+ */ -+ mipv6_leave_sol_mc_addr(&curr->ll_haddr, -+ curr->ifp->idev->dev); -+ if (ipv6_addr_cmp(&curr->ll_haddr, &curr->haddr)) -+ mipv6_leave_sol_mc_addr(&curr->haddr, -+ curr->ifp->idev->dev); -+ } -+ in6_ifa_put(curr->ifp); -+ kfree(curr); -+ curr = next; -+ } -+ dad_cell_head.next = dad_cell_head.prev = &dad_cell_head; -+ write_unlock_bh(&dad_lock); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/prefix.c -@@ -0,0 +1,217 @@ -+/** -+ * Prefix solicitation and advertisement -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/icmpv6.h> -+#include <linux/net.h> -+#include <linux/spinlock.h> -+#include <linux/timer.h> -+#include <linux/netdevice.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "mipv6_icmp.h" -+#include "debug.h" -+#include "sortedlist.h" -+#include "prefix.h" -+#include "config.h" -+ -+#define INFINITY 0xffffffff -+ -+struct timer_list pfx_timer; -+ -+struct list_head pfx_list; -+rwlock_t pfx_list_lock = RW_LOCK_UNLOCKED; -+ -+int compare_pfx_list_entry(const void *data1, const void *data2, -+ int datalen) -+{ -+ struct pfx_list_entry *e1 = (struct pfx_list_entry *) data1; -+ struct pfx_list_entry *e2 = (struct pfx_list_entry *) data2; -+ -+ return ((ipv6_addr_cmp(&e1->daddr, &e2->daddr) == 0) -+ && (e2->ifindex == -1 || e1->ifindex == e2->ifindex)); -+} -+ -+/** -+ * mipv6_pfx_cancel_send - cancel pending items to daddr from saddr -+ * @daddr: Destination address -+ * @ifindex: pending items on this interface will be canceled -+ * -+ * if ifindex == -1, all items to daddr will be removed -+ */ -+void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex) -+{ -+ unsigned long tmp; -+ struct pfx_list_entry entry; -+ -+ DEBUG_FUNC(); -+ -+ /* We'll just be comparing these parts... */ -+ memcpy(&entry.daddr, daddr, sizeof(struct in6_addr)); -+ entry.ifindex = ifindex; -+ -+ write_lock_bh(&pfx_list_lock); -+ -+ while (mipv6_slist_del_item(&pfx_list, &entry, -+ compare_pfx_list_entry) == 0) -+ ; -+ -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ -+ write_unlock_bh(&pfx_list_lock); -+} -+ -+/** -+ * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to -+ * @daddr: address of HA -+ * @saddr: our address to use as source address -+ * @ifindex: interface index -+ */ -+void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr, -+ int ifindex) -+{ -+ unsigned long tmp; -+ struct pfx_list_entry entry; -+ -+ DEBUG_FUNC(); -+ -+ memcpy(&entry.daddr, daddr, sizeof(struct in6_addr)); -+ memcpy(&entry.saddr, saddr, sizeof(struct in6_addr)); -+ entry.retries = 0; -+ entry.ifindex = ifindex; -+ -+ write_lock_bh(&pfx_list_lock); -+ if (mipv6_slist_modify(&pfx_list, &entry, sizeof(struct pfx_list_entry), -+ jiffies + INITIAL_SOLICIT_TIMER * HZ, -+ compare_pfx_list_entry)) -+ DEBUG(DBG_WARNING, "Cannot add new HA to pfx list"); -+ -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ write_unlock_bh(&pfx_list_lock); -+} -+ -+int mipv6_pfx_add_home(int ifindex, struct in6_addr *saddr, -+ struct in6_addr *daddr, unsigned long min_expire) -+{ -+ unsigned long tmp; -+ -+ write_lock(&pfx_list_lock); -+ -+ if (min_expire != INFINITY) { -+ unsigned long expire; -+ struct pfx_list_entry entry; -+ -+ memcpy(&entry.daddr, saddr, sizeof(struct in6_addr)); -+ memcpy(&entry.saddr, daddr, sizeof(struct in6_addr)); -+ entry.retries = 0; -+ entry.ifindex = ifindex; -+ -+ /* This is against the RFC 3775, but we need to set -+ * a minimum interval for a prefix solicitation. -+ * Otherwise a prefix solicitation storm will -+ * result if valid lifetime of the prefix is -+ * smaller than MAX_PFX_ADV_DELAY -+ */ -+ min_expire -= MAX_PFX_ADV_DELAY; -+ min_expire = min_expire < MIN_PFX_SOL_DELAY ? MIN_PFX_SOL_DELAY : min_expire; -+ -+ expire = jiffies + min_expire * HZ; -+ -+ if (mipv6_slist_modify(&pfx_list, &entry, -+ sizeof(struct pfx_list_entry), -+ expire, -+ compare_pfx_list_entry) != 0) -+ DEBUG(DBG_WARNING, "Cannot add new entry to pfx_list"); -+ } -+ -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ -+ write_unlock(&pfx_list_lock); -+ -+ return 0; -+} -+ -+/** -+ * set_ha_pfx_list - manipulate pfx_list for HA when timer goes off -+ * @entry: pfx_list_entry that is due -+ */ -+static void set_ha_pfx_list(struct pfx_list_entry *entry) -+{ -+} -+ -+/** -+ * set_mn_pfx_list - manipulate pfx_list for MN when timer goes off -+ * @entry: pfx_list_entry that is due -+ */ -+static void set_mn_pfx_list(struct pfx_list_entry *entry) -+{ -+} -+ -+/** -+ * pfx_timer_handler - general timer handler -+ * @dummy: dummy -+ * -+ * calls set_ha_pfx_list and set_mn_pfx_list to do the thing when -+ * a timer goes off -+ */ -+static void pfx_timer_handler(unsigned long dummy) -+{ -+ unsigned long tmp; -+ struct pfx_list_entry *entry; -+ -+ DEBUG_FUNC(); -+ -+ write_lock(&pfx_list_lock); -+ if (!(entry = mipv6_slist_get_first(&pfx_list))) -+ goto out; -+ -+ if (mip6node_cnf.capabilities & CAP_HA) -+ set_ha_pfx_list(entry); -+ if (mip6node_cnf.capabilities & CAP_MN) -+ set_mn_pfx_list(entry); -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ -+ out: -+ write_unlock(&pfx_list_lock); -+} -+ -+int mipv6_initialize_pfx_icmpv6(void) -+{ -+ INIT_LIST_HEAD(&pfx_list); -+ -+ init_timer(&pfx_timer); -+ pfx_timer.function = pfx_timer_handler; -+ -+ return 0; -+} -+ -+void mipv6_shutdown_pfx_icmpv6(void) -+{ -+ struct prefix_info *tmp; -+ -+ if (timer_pending(&pfx_timer)) -+ del_timer(&pfx_timer); -+ -+ write_lock_bh(&pfx_list_lock); -+ while ((tmp = mipv6_slist_del_first(&pfx_list))) -+ kfree(tmp); -+ write_unlock_bh(&pfx_list_lock); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/prefix.h -@@ -0,0 +1,57 @@ -+/* -+ * MIPL Mobile IPv6 Prefix solicitation and advertisement -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _PREFIX_H -+#define _PREFIX_H -+ -+#include <net/addrconf.h> -+ -+struct pfx_list_entry { -+ struct in6_addr daddr; -+ struct in6_addr saddr; -+ int retries; -+ int ifindex; -+}; -+ -+extern struct list_head pfx_list; -+extern rwlock_t pfx_list_lock; -+extern struct timer_list pfx_timer; -+ -+int compare_pfx_list_entry(const void *data1, const void *data2, -+ int datalen); -+ -+/** -+ * mipv6_pfx_cancel_send - cancel pending pfx_advs/sols to daddr -+ * @daddr: destination address -+ * @ifindex: pending items on this interface will be canceled -+ * -+ * if ifindex == -1, all items to daddr will be removed -+ */ -+void mipv6_pfx_cancel_send(struct in6_addr *daddr, int ifindex); -+ -+/** -+ * mipv6_pfx_add_ha - add a new HA to send prefix solicitations to -+ * @daddr: address of HA -+ * @saddr: our address to use as source address -+ * @ifindex: interface index -+ */ -+void mipv6_pfx_add_ha(struct in6_addr *daddr, struct in6_addr *saddr, -+ int ifindex); -+ -+void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex); -+ -+int mipv6_pfx_add_home(int ifindex, struct in6_addr *daddr, -+ struct in6_addr *saddr, unsigned long min_expire); -+ -+int mipv6_initialize_pfx_icmpv6(void); -+void mipv6_shutdown_pfx_icmpv6(void); -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/prefix_ha.c -@@ -0,0 +1,122 @@ -+/** -+ * Prefix advertisement for Home Agent -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/config.h> -+#include <linux/icmpv6.h> -+#include <linux/net.h> -+#include <linux/spinlock.h> -+#include <linux/timer.h> -+#include <linux/netdevice.h> -+#include <net/ipv6.h> -+#include <net/addrconf.h> -+#include <net/ip6_route.h> -+#include <net/mipv6.h> -+ -+#include "mipv6_icmp.h" -+#include "debug.h" -+#include "sortedlist.h" -+#include "util.h" -+#include "bcache.h" -+#include "config.h" -+#include "prefix.h" -+ -+/** -+ * pfx_adv_iterator - modify pfx_list entries according to new prefix info -+ * @data: MN's home registration bcache_entry -+ * @args: new prefix info -+ * @sortkey: ignored -+ */ -+static int pfx_adv_iterator(void *data, void *args, unsigned long sortkey) -+{ -+ struct mipv6_bce *bc_entry = (struct mipv6_bce *) data; -+ struct prefix_info *pinfo = (struct prefix_info *) args; -+ -+ if (mipv6_prefix_compare(&bc_entry->coa, &pinfo->prefix, -+ pinfo->prefix_len) == 0) { -+ struct pfx_list_entry pfx_entry; -+ -+ memcpy(&pfx_entry.daddr, &bc_entry->coa, -+ sizeof(struct in6_addr)); -+ memcpy(&pfx_entry.daddr, &bc_entry->our_addr, -+ sizeof(struct in6_addr)); -+ pfx_entry.retries = 0; -+ pfx_entry.ifindex = bc_entry->ifindex; -+ -+ mipv6_slist_modify(&pfx_list, &pfx_entry, -+ sizeof(struct pfx_list_entry), -+ jiffies + -+ net_random() % (MAX_PFX_ADV_DELAY * HZ), -+ compare_pfx_list_entry); -+ } -+ -+ return 0; -+} -+ -+struct homereg_iterator_args { -+ struct list_head *head; -+ int count; -+}; -+ -+static int homereg_iterator(void *data, void *args, unsigned long *sortkey) -+{ -+ struct mipv6_bce *entry = (struct mipv6_bce *) data; -+ struct homereg_iterator_args *state = -+ (struct homereg_iterator_args *) args; -+ -+ if (entry->type == HOME_REGISTRATION) { -+ mipv6_slist_add(state->head, entry, -+ sizeof(struct mipv6_bce), -+ state->count); -+ state->count++; -+ } -+ return 0; -+} -+ -+static int mipv6_bcache_get_homeregs(struct list_head *head) -+{ -+ struct homereg_iterator_args args; -+ -+ DEBUG_FUNC(); -+ -+ args.count = 0; -+ args.head = head; -+ -+ mipv6_bcache_iterate(homereg_iterator, &args); -+ return args.count; -+} -+ -+/** -+ * mipv6_prefix_added - prefix was added to interface, act accordingly -+ * @pinfo: prefix_info that was added -+ * @ifindex: interface index -+ */ -+void mipv6_pfxs_modified(struct prefix_info *pinfo, int ifindex) -+{ -+ int count; -+ unsigned long tmp; -+ struct list_head home_regs; -+ -+ DEBUG_FUNC(); -+ -+ INIT_LIST_HEAD(&home_regs); -+ -+ if (!(count = mipv6_bcache_get_homeregs(&home_regs))) -+ return; -+ -+ write_lock_bh(&pfx_list_lock); -+ mipv6_slist_for_each(&home_regs, pinfo, pfx_adv_iterator); -+ if ((tmp = mipv6_slist_get_first_key(&pfx_list))) -+ mod_timer(&pfx_timer, tmp); -+ write_unlock_bh(&pfx_list_lock); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/rr_crypto.c -@@ -0,0 +1,255 @@ -+/* -+ * rr_cookie.c - Mobile IPv6 return routability crypto -+ * Author : Henrik Petander <henrik.petander@hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * -+ * -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/spinlock.h> -+#include <linux/sched.h> -+#include <linux/timer.h> -+#include <linux/in6.h> -+#include <linux/init.h> -+#include <linux/random.h> -+ -+#include <net/ipv6.h> -+ -+#include "debug.h" -+#include "hmac.h" -+#include "rr_crypto.h" -+ -+#define DBG_RR 5 -+ -+u8 k_CN[HMAC_SHA1_KEY_SIZE]; // secret key of CN -+ -+u16 curr_index = 0; -+ -+struct nonce_timestamp nonce_table[MAX_NONCES]; -+spinlock_t nonce_lock = SPIN_LOCK_UNLOCKED; -+void update_nonces(void); -+ -+/** nonce_is_fresh - whether the nonce was generated recently -+ * -+ * @non_ts : table entry containing the nonce and a timestamp -+ * @interval : if nonce was generated within interval seconds it is fresh -+ * -+ * Returns 1 if the nonce is fresh, 0 otherwise. -+ */ -+static int nonce_is_fresh(struct nonce_timestamp *non_ts, unsigned long interval) -+{ -+ if (time_before(jiffies, non_ts->timestamp + interval * HZ) && !non_ts->invalid) -+ return 1; -+ return 0; -+} -+void mipv6_rr_invalidate_nonce(u16 nonce_ind) -+{ -+ spin_lock_bh(&nonce_lock); -+ if (nonce_ind > MAX_NONCES) { -+ spin_unlock_bh(&nonce_lock); -+ return; -+ } -+ nonce_table[nonce_ind].invalid = 1; -+ spin_unlock_bh(&nonce_lock); -+} -+/* Returns a pointer to a new nonce */ -+struct mipv6_rr_nonce * mipv6_rr_get_new_nonce(void) -+{ -+ struct mipv6_rr_nonce *nce = kmalloc(sizeof(*nce), GFP_ATOMIC); -+ -+ if (!nce) -+ return NULL; -+ // Lock nonces here -+ spin_lock_bh(&nonce_lock); -+ // If nonce is not fresh create new one -+ if (!nonce_is_fresh(&nonce_table[curr_index], MIPV6_RR_NONCE_LIFETIME)) { -+ // increment the last nonce pointer and create new nonce -+ curr_index++; -+ // Wrap around -+ if (curr_index == MAX_NONCES) -+ curr_index = 0; -+ // Get random data to fill the nonce data -+ get_random_bytes(nonce_table[curr_index].nonce.data, MIPV6_RR_NONCE_DATA_LENGTH); -+ // Fill the index field -+ nonce_table[curr_index].nonce.index = curr_index; -+ nonce_table[curr_index].invalid = 0; -+ nonce_table[curr_index].timestamp = jiffies; -+ } -+ spin_unlock_bh(&nonce_lock); -+ memcpy(nce, &nonce_table[curr_index].nonce, sizeof(*nce)); -+ // Unlock nonces -+ return nce; -+} -+/** mipv6_rr_nonce_get_by_index - returns a nonce for index -+ * @nonce_ind : index of the nonce -+ * -+ * Returns a nonce or NULL if the nonce index was invalid or the nonce -+ * for the index was not fresh. -+ */ -+struct mipv6_rr_nonce * mipv6_rr_nonce_get_by_index(u16 nonce_ind) -+{ -+ struct mipv6_rr_nonce *nce = NULL; -+ -+ spin_lock_bh(&nonce_lock); -+ if (nonce_ind >= MAX_NONCES) { -+ DEBUG(DBG_WARNING, "Nonce index field from BU invalid"); -+ -+ /* Here a double of the nonce_lifetime is used for freshness -+ * verification, since the nonces -+ * are not created in response to every initiator packet -+ */ -+ } else if (nonce_is_fresh(&nonce_table[nonce_ind], 2 * MIPV6_RR_NONCE_LIFETIME)) { -+ nce = kmalloc(sizeof(*nce), GFP_ATOMIC); -+ memcpy(nce, &nonce_table[nonce_ind].nonce, sizeof(*nce)); -+ } -+ spin_unlock_bh(&nonce_lock); -+ -+ return nce; -+} -+ -+/* Fills rr test init cookies with random bytes */ -+void mipv6_rr_mn_cookie_create(u8 *cookie) -+{ -+ get_random_bytes(cookie, MIPV6_RR_COOKIE_LENGTH); -+} -+ -+/** mipv6_rr_cookie_create - builds a home or care-of cookie -+ * -+ * @addr : the home or care-of address from HoTI or CoTI -+ * @ckie : memory where the cookie is copied to -+ * @nce : pointer to a nonce used for the calculation, nce is freed during the function -+ * -+ */ -+int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie, -+ u16 nonce_index) -+{ -+ struct ah_processing ah_proc; -+ u8 digest[HMAC_SHA1_HASH_LEN]; -+ struct mipv6_rr_nonce *nce; -+ -+ if ((nce = mipv6_rr_nonce_get_by_index(nonce_index))== NULL) -+ return -1; -+ -+ if (*ckie == NULL && (*ckie = kmalloc(MIPV6_RR_COOKIE_LENGTH, -+ GFP_ATOMIC)) == NULL) { -+ kfree(nce); -+ return -1; -+ } -+ /* Calculate the full hmac-sha1 digest from address and nonce using the secret key of cn */ -+ -+ if (ah_hmac_sha1_init(&ah_proc, k_CN, HMAC_SHA1_KEY_SIZE) < 0) { -+ DEBUG(DBG_ERROR, "Hmac sha1 initialization failed"); -+ kfree(nce); -+ return -1; -+ } -+ -+ ah_hmac_sha1_loop(&ah_proc, addr, sizeof(*addr)); -+ ah_hmac_sha1_loop(&ah_proc, nce->data, MIPV6_RR_NONCE_DATA_LENGTH); -+ ah_hmac_sha1_result(&ah_proc, digest); -+ -+ -+ /* clean up nonce */ -+ kfree(nce); -+ -+ /* Copy first 64 bits of hash target to the cookie */ -+ memcpy(*ckie, digest, MIPV6_RR_COOKIE_LENGTH); -+ return 0; -+} -+ -+/** mipv6_rr_key_calc - creates BU authentication key -+ * -+ * @hoc : Home Cookie -+ * @coc : Care-of Cookie -+ * -+ * Returns BU authentication key of length HMAC_SHA1_KEY_SIZE or NULL in error cases, -+ * caller needs to free the key. -+ */ -+u8 *mipv6_rr_key_calc(u8 *hoc, u8 *coc) -+{ -+ -+ u8 *key_bu = kmalloc(HMAC_SHA1_KEY_SIZE, GFP_ATOMIC); -+ SHA1_CTX c; -+ -+ if (!key_bu) { -+ DEBUG(DBG_CRITICAL, "Memory allocation failed, could nort create BU authentication key"); -+ return NULL; -+ } -+ -+ /* Calculate the key from home and care-of cookies -+ * Kbu = sha1(home_cookie | care-of cookie) -+ * or KBu = sha1(home_cookie), if MN deregisters -+ */ -+ sha1_init(&c); -+ sha1_compute(&c, hoc, MIPV6_RR_COOKIE_LENGTH); -+ if (coc) -+ sha1_compute(&c, coc, MIPV6_RR_COOKIE_LENGTH); -+ sha1_final(&c, key_bu); -+ DEBUG(DBG_RR, "Home and Care-of cookies used for calculating key "); -+ debug_print_buffer(DBG_RR, hoc, MIPV6_RR_COOKIE_LENGTH); -+ if (coc) -+ debug_print_buffer(DBG_RR, coc, MIPV6_RR_COOKIE_LENGTH); -+ -+ return key_bu; -+} -+ -+void mipv6_rr_init(void) -+{ -+ get_random_bytes(k_CN, HMAC_SHA1_KEY_SIZE); -+ memset(nonce_table, 0, MAX_NONCES * sizeof(struct nonce_timestamp)); -+} -+ -+#ifdef TEST_MIPV6_RR_CRYPTO -+void mipv6_test_rr(void) -+{ -+ struct mipv6_rr_nonce *nonce; -+ struct in6_addr a1, a2; -+ int ind1, ind2; -+ u8 *ckie1 = NULL, *ckie2 = NULL; -+ u8 *key_mn = NULL, *key_cn = NULL; -+ mipv6_init_rr(); -+ -+ nonce = mipv6_rr_get_new_nonce(); -+ if (!nonce) { -+ printk("mipv6_rr_get_new_nonce() failed, at 1! \n"); -+ return; -+ } -+ mipv6_rr_cookie_create(&a1, &ckie1, nonce->index); -+ ind1 = nonce->index; -+ kfree(nonce); -+ -+ nonce = mipv6_rr_get_new_nonce(); -+ if (!nonce) { -+ printk("mipv6_rr_get_new_nonce() failed, at 2! \n"); -+ return; -+ } -+ -+ mipv6_rr_cookie_create(&a2, &ckie2, nonce->index); -+ ind2 = nonce->index; -+ key_mn = mipv6_rr_key_calc(ckie1, ckie2); -+ -+ /* Create home and coa cookies based on indices */ -+ mipv6_rr_cookie_create(&a1, &ckie1, ind1); -+ mipv6_rr_cookie_create(&a2, &ckie2, ind2); -+ key_cn = mipv6_rr_key_calc(ckie1, ckie2); -+ if (!key_cn || !key_mn) { -+ printk("creation of secret key failed!\n"); -+ return; -+ } -+ if(memcmp(key_cn, key_mn, HMAC_SHA1_KEY_SIZE)) -+ printk("mipv6_rr_key_calc produced different keys for MN and CN \n"); -+ else -+ printk("mipv6_rr_crypto test OK\n"); -+ kfree(nonce); -+ kfree(key_cn); -+ kfree(key_mn); -+} -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/rr_crypto.h -@@ -0,0 +1,72 @@ -+/* -+ * MIPL Mobile IPv6 Return routability crypto prototypes -+ * -+ * $Id:$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _RR_CRYPTO -+#define _RR_CRYPTO -+ -+#include <linux/in6.h> -+ -+/* Macros and data structures */ -+ -+#define MIPV6_RR_NONCE_LIFETIME 60 -+#define MIPV6_RR_NONCE_DATA_LENGTH 8 -+#define MIPV6_RR_COOKIE_LENGTH 8 -+#define COOKIE_SIZE 8 -+#define MAX_NONCES 4 -+#define HMAC_SHA1_KEY_SIZE 20 -+ -+struct mipv6_rr_nonce { -+ u_int16_t index; -+ u_int8_t data[MIPV6_RR_NONCE_DATA_LENGTH]; -+}; -+ -+struct nonce_timestamp { -+ struct mipv6_rr_nonce nonce; -+ unsigned long timestamp; -+ u_int8_t invalid; -+}; -+ -+/* Function definitions */ -+ -+/* Return 1 if equal, 0 if not */ -+static __inline__ int mipv6_equal_cookies(u8 *c1, u8 *c2) -+{ -+ return (memcmp(c1, c2, MIPV6_RR_COOKIE_LENGTH) == 0); -+} -+ -+/* Function declarations */ -+ -+/* Create cookie for HoTi and CoTi */ -+extern void mipv6_rr_mn_cookie_create(u8 *cookie); -+ -+/* Create cookie for HoT and CoT */ -+extern int mipv6_rr_cookie_create(struct in6_addr *addr, u8 **ckie, u16 nonce_index); -+ -+/* Calculate return routability key from home and care-of cookies, key length is -+ * HMAC_SHA1_KEY_SIZE -+ */ -+extern u_int8_t *mipv6_rr_key_calc(u8 *hoc, u8 *coc); -+ -+extern struct mipv6_rr_nonce *mipv6_rr_get_new_nonce(void); -+ -+/* For avoiding replay attacks when MN deregisters */ -+extern void mipv6_rr_invalidate_nonce(u16 nonce_index); -+/* -+ * initializes the return routability crypto -+ */ -+ -+void mipv6_rr_init(void); -+ -+#ifdef TEST_MIPV6_RR_CRYPTO -+void mipv6_test_rr(void); -+#endif /* TEST_MIPV6_RR_CRYPTO */ -+ -+#endif /* RR_CRYPTO */ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/sortedlist.c -@@ -0,0 +1,349 @@ -+/** -+ * Sorted list - linked list with sortkey. -+ * -+ * Authors: -+ * Jaakko Laine <medved@iki.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#include <linux/kernel.h> -+#include <linux/types.h> -+#include <linux/list.h> -+#include <linux/slab.h> -+#include <linux/spinlock.h> -+#include <linux/string.h> -+ -+struct mipv6_sorted_list_entry { -+ struct list_head list; -+ void *data; -+ int datalen; -+ unsigned long sortkey; -+}; -+ -+/** -+ * compare - compares two arbitrary data items -+ * @data1: first data item -+ * @data2: second data item -+ * @datalen: length of data items in bits -+ * -+ * datalen is in bits! -+ */ -+int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen) -+{ -+ int n = datalen; -+ __u8 * ptr1 = (__u8 *)data1; -+ __u8 * ptr2 = (__u8 *)data2; -+ -+ for (; n>=0; n-=8, ptr1++, ptr2++) { -+ if (n >= 8) { -+ if (*ptr1 != *ptr2) -+ return 0; -+ } else { -+ if ((*ptr1 ^ *ptr2) & ((~0) << (8 - n))) -+ return 0; -+ } -+ } -+ -+ return 1; -+} -+ -+/** -+ * mipv6_slist_add - add an entry to sorted list -+ * @head: list_head of the sorted list -+ * @data: item to store -+ * @datalen: length of data (in bytes) -+ * @key: sortkey of item -+ * -+ * Allocates memory for entry and data -+ */ -+int mipv6_slist_add(struct list_head *head, void *data, int datalen, -+ unsigned long sortkey) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry, *tmp, *next; -+ -+ entry = kmalloc(sizeof(struct mipv6_sorted_list_entry), GFP_ATOMIC); -+ -+ if (!entry) -+ return -1; -+ -+ entry->data = kmalloc(datalen, GFP_ATOMIC); -+ -+ if (!entry->data) { -+ kfree(entry); -+ return -1; -+ } -+ -+ memcpy(entry->data, data, datalen); -+ entry->datalen = datalen; -+ entry->sortkey = sortkey; -+ -+ if ((pos = head->next) == head) { -+ list_add(&entry->list, head); -+ return 0; -+ } -+ -+ tmp = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (entry->sortkey < tmp->sortkey) { -+ list_add(&entry->list, head); -+ return 0; -+ } -+ -+ for (; pos != head; pos = pos->next) { -+ tmp = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (pos->next == head) { -+ list_add(&entry->list, &tmp->list); -+ return 0; -+ } -+ next = list_entry(pos->next, struct mipv6_sorted_list_entry, list); -+ if (entry->sortkey >= tmp->sortkey && entry->sortkey < next->sortkey) { -+ list_add(&entry->list, &tmp->list); -+ return 0; -+ } -+ } -+ -+ /* never reached */ -+ return -1; -+} -+ -+/** -+ * mipv6_slist_get_first - get the first data item in the list -+ * @head: list_head of the sorted list -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_first(struct list_head *head) -+{ -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return NULL; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ return entry->data; -+} -+ -+/** -+ * mipv6_slist_del_first - delete (and get) the first item in list -+ * @head: list_head of the sorted list -+ * -+ * Remember to kfree the item -+ */ -+void *mipv6_slist_del_first(struct list_head *head) -+{ -+ void *tmp; -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return NULL; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ tmp = entry->data; -+ -+ list_del(head->next); -+ kfree(entry); -+ -+ return tmp; -+} -+ -+/** -+ * mipv6_slist_del_item - delete entry -+ * @head: list_head of the sorted list -+ * @data: item to delete -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) -+ */ -+int mipv6_slist_del_item(struct list_head *head, void *data, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ for(pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (compare(data, entry->data, entry->datalen)) { -+ list_del(pos); -+ kfree(entry->data); -+ kfree(entry); -+ return 0; -+ } -+ } -+ -+ return -1; -+} -+ -+/** -+ * mipv6_slist_get_first_key - get sortkey of the first item -+ * @head: list_head of the sorted list -+ */ -+unsigned long mipv6_slist_get_first_key(struct list_head *head) -+{ -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return 0; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ return entry->sortkey; -+} -+ -+/** -+ * mipv6_slist_get_key - get sortkey of the data item -+ * @head: list_head of the sorted list -+ * @data: the item to search for -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) -+ */ -+unsigned long mipv6_slist_get_key(struct list_head *head, void *data, -+ int (*compare)(const void *data1, -+ const void *data2, -+ int datalen)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ for(pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (compare(data, entry->data, entry->datalen)) -+ return entry->sortkey; -+ } -+ -+ return 0; -+} -+ -+/** -+ * mipv6_slist_get_data - get the data item identified by sortkey -+ * @head: list_head of the sorted list -+ * @key: sortkey of the item -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ list_for_each(pos, head) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (entry->sortkey == sortkey) -+ return entry->data; -+ } -+ -+ return NULL; -+} -+ -+/** -+ * reorder_entry - move an entry to a new position according to sortkey -+ * @head: list_head of the sorted list -+ * @entry_pos: current place of the entry -+ * @key: new sortkey -+ */ -+static void reorder_entry(struct list_head *head, struct list_head *entry_pos, -+ unsigned long sortkey) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ list_del(entry_pos); -+ -+ for (pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (sortkey >= entry->sortkey) { -+ list_add(entry_pos, &entry->list); -+ return; -+ } -+ } -+ -+ list_add(entry_pos, head); -+} -+ -+/** -+ * mipv6_slist_modify - modify data item -+ * @head: list_head of the sorted list -+ * @data: item, whose sortkey is to be modified -+ * @datalen: datalen in bytes -+ * @new_key: new sortkey -+ * @compare: function used for comparing the data items -+ * -+ * Compies the new data on top of the old one, if compare function returns -+ * true. If there's no matching entry, new one will be created. -+ * Compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) -+ */ -+int mipv6_slist_modify(struct list_head *head, void *data, int datalen, -+ unsigned long new_key, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ for (pos = head->next; pos != head; pos = pos->next) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (compare(data, entry->data, datalen)) { -+ memcpy(entry->data, data, datalen); -+ entry->sortkey = new_key; -+ reorder_entry(head, &entry->list, new_key); -+ return 0; -+ } -+ } -+ -+ return mipv6_slist_add(head, data, datalen, new_key); -+} -+ -+/** -+ * mipv6_slist_push_first - move the first entry to place indicated by new_key -+ * @head: list_head of the sorted list -+ * @new_key: new sortkey -+ */ -+int mipv6_slist_push_first(struct list_head *head, unsigned long new_key) -+{ -+ struct mipv6_sorted_list_entry *entry; -+ -+ if (list_empty(head)) -+ return -1; -+ -+ entry = list_entry(head->next, struct mipv6_sorted_list_entry, list); -+ entry->sortkey = new_key; -+ -+ reorder_entry(head, head->next, new_key); -+ return 0; -+} -+ -+/** -+ * mipv6_slist_for_each - apply func to every item in list -+ * @head: list_head of the sorted list -+ * @args: args to pass to func -+ * @func: function to use -+ * -+ * function must be of type -+ * int (*func)(void *data, void *args, unsigned long sortkey) -+ * List iteration will stop once func has been applied to every item -+ * or when func returns true -+ */ -+int mipv6_slist_for_each(struct list_head *head, void *args, -+ int (*func)(void *data, void *args, -+ unsigned long sortkey)) -+{ -+ struct list_head *pos; -+ struct mipv6_sorted_list_entry *entry; -+ -+ list_for_each(pos, head) { -+ entry = list_entry(pos, struct mipv6_sorted_list_entry, list); -+ if (func(entry->data, args, entry->sortkey)) -+ break; -+ } -+ -+ return 0; -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/sortedlist.h -@@ -0,0 +1,133 @@ -+/* -+ * Sorted list - linked list with sortkey -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+/** -+ * compare - compares two arbitrary data items -+ * @data1: first data item -+ * @data2: second data item -+ * @datalen: length of data items in bits -+ * -+ * datalen is in bits! -+ */ -+int mipv6_bitwise_compare(const void *data1, const void *data2, int datalen); -+ -+/** -+ * mipv6_slist_add - add an entry to sorted list -+ * @head: list_head of the sorted list -+ * @data: item to store -+ * @datalen: length of data (in bytes) -+ * @key: sortkey of item -+ * -+ * Allocates memory for entry and data -+ */ -+int mipv6_slist_add(struct list_head *head, void *data, int datalen, -+ unsigned long sortkey); -+ -+/** -+ * mipv6_slist_get_first - get the first data item in the list -+ * @head: list_head of the sorted list -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_first(struct list_head *head); -+ -+/** -+ * mipv6_slist_del_first - delete (and get) the first item in list -+ * @head: list_head of the sorted list -+ * -+ * Remember to kfree the item -+ */ -+void *mipv6_slist_del_first(struct list_head *head); -+ -+/** -+ * mipv6_slist_del_item - delete entry -+ * @head: list_head of the sorted list -+ * @data: item to delete -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits -+ */ -+int mipv6_slist_del_item(struct list_head *head, void *data, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_get_first_key - get sortkey of the first item -+ * @head: list_head of the sorted list -+ */ -+unsigned long mipv6_slist_get_first_key(struct list_head *head); -+ -+/** -+ * mipv6_slist_get_key - get sortkey of the data item -+ * @head: list_head of the sorted list -+ * @data: the item to search for -+ * @compare: function used for comparing the data items -+ * -+ * compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits -+ */ -+unsigned long mipv6_slist_get_key(struct list_head *head, void *data, -+ int (*compare)(const void *data1, -+ const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_get_data - get the data item identified by sortkey -+ * @head: list_head of the sorted list -+ * @key: sortkey of the item -+ * -+ * Returns the actual data item, not copy, so don't kfree it -+ */ -+void *mipv6_slist_get_data(struct list_head *head, unsigned long sortkey); -+ -+/** -+ * mipv6_slist_modify - modify data item -+ * @head: list_head of the sorted list -+ * @data: item, whose sortkey is to be modified -+ * @datalen: datalen in bytes -+ * @new_key: new sortkey -+ * @compare: function used for comparing the data items -+ * -+ * Compies the new data on top of the old one, if compare function returns -+ * non-negative. If there's no matching entry, new one will be created. -+ * Compare function needs to have prototype -+ * int (*compare)(const void *data1, const void *data2, int datalen) where -+ * datalen is in bits. -+ */ -+int mipv6_slist_modify(struct list_head *head, void *data, int datalen, -+ unsigned long new_key, -+ int (*compare)(const void *data1, const void *data2, -+ int datalen)); -+ -+/** -+ * mipv6_slist_push_first - move the first entry to place indicated by new_key -+ * @head: list_head of the sorted list -+ * @new_key: new sortkey -+ */ -+int mipv6_slist_push_first(struct list_head *head, unsigned long new_key); -+ -+/** -+ * mipv6_slist_for_each - apply func to every item in list -+ * @head: list_head of the sorted list -+ * @args: args to pass to func -+ * @func: function to use -+ * -+ * function must be of type -+ * int (*func)(void *data, void *args, unsigned long sortkey) -+ * List iteration will stop once func has been applied to every item -+ * or when func returns true -+ */ -+int mipv6_slist_for_each(struct list_head *head, void *args, -+ int (*func)(void *data, void *args, -+ unsigned long sortkey)); ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/stats.c -@@ -0,0 +1,90 @@ -+/* -+ * Statistics module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ * Changes: -+ * Krishna Kumar, -+ * Venkata Jagana : SMP locking fix -+ */ -+ -+#include <linux/config.h> -+#include <linux/proc_fs.h> -+#include "stats.h" -+ -+struct mipv6_statistics mipv6_stats; -+ -+static int proc_info_dump( -+ char *buffer, char **start, -+ off_t offset, int length) -+{ -+ struct inf { -+ char *name; -+ int *value; -+ } int_stats[] = { -+ {"NEncapsulations", &mipv6_stats.n_encapsulations}, -+ {"NDecapsulations", &mipv6_stats.n_decapsulations}, -+ {"NBindRefreshRqsRcvd", &mipv6_stats.n_brr_rcvd}, -+ {"NHomeTestInitsRcvd", &mipv6_stats.n_hoti_rcvd}, -+ {"NCareofTestInitsRcvd", &mipv6_stats.n_coti_rcvd}, -+ {"NHomeTestRcvd", &mipv6_stats.n_hot_rcvd}, -+ {"NCareofTestRcvd", &mipv6_stats.n_cot_rcvd}, -+ {"NBindUpdatesRcvd", &mipv6_stats.n_bu_rcvd}, -+ {"NBindAcksRcvd", &mipv6_stats.n_ba_rcvd}, -+ {"NBindNAcksRcvd", &mipv6_stats.n_ban_rcvd}, -+ {"NBindErrorsRcvd", &mipv6_stats.n_be_rcvd}, -+ {"NBindRefreshRqsSent", &mipv6_stats.n_brr_sent}, -+ {"NHomeTestInitsSent", &mipv6_stats.n_hoti_sent}, -+ {"NCareofTestInitsSent", &mipv6_stats.n_coti_sent}, -+ {"NHomeTestSent", &mipv6_stats.n_hot_sent}, -+ {"NCareofTestSent", &mipv6_stats.n_cot_sent}, -+ {"NBindUpdatesSent", &mipv6_stats.n_bu_sent}, -+ {"NBindAcksSent", &mipv6_stats.n_ba_sent}, -+ {"NBindNAcksSent", &mipv6_stats.n_ban_sent}, -+ {"NBindErrorsSent", &mipv6_stats.n_be_sent}, -+ {"NBindUpdatesDropAuth", &mipv6_stats.n_bu_drop.auth}, -+ {"NBindUpdatesDropInvalid", &mipv6_stats.n_bu_drop.invalid}, -+ {"NBindUpdatesDropMisc", &mipv6_stats.n_bu_drop.misc}, -+ {"NBindAcksDropAuth", &mipv6_stats.n_bu_drop.auth}, -+ {"NBindAcksDropInvalid", &mipv6_stats.n_bu_drop.invalid}, -+ {"NBindAcksDropMisc", &mipv6_stats.n_bu_drop.misc}, -+ {"NBindRqsDropAuth", &mipv6_stats.n_bu_drop.auth}, -+ {"NBindRqsDropInvalid", &mipv6_stats.n_bu_drop.invalid}, -+ {"NBindRqsDropMisc", &mipv6_stats.n_bu_drop.misc} -+ }; -+ -+ int i; -+ int len = 0; -+ for(i=0; i<sizeof(int_stats) / sizeof(struct inf); i++) { -+ len += sprintf(buffer + len, "%s = %d\n", -+ int_stats[i].name, *int_stats[i].value); -+ } -+ -+ *start = buffer + offset; -+ -+ len -= offset; -+ -+ if(len > length) len = length; -+ -+ return len; -+} -+ -+int mipv6_stats_init(void) -+{ -+ memset(&mipv6_stats, 0, sizeof(struct mipv6_statistics)); -+ proc_net_create("mip6_stat", 0, proc_info_dump); -+ return 0; -+} -+ -+void mipv6_stats_exit(void) -+{ -+ proc_net_remove("mip6_stat"); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/stats.h -@@ -0,0 +1,71 @@ -+/* -+ * MIPL Mobile IPv6 Statistics header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _STATS_H -+#define _STATS_H -+ -+struct mipv6_drop { -+ __u32 auth; -+ __u32 invalid; -+ __u32 misc; -+}; -+ -+struct mipv6_statistics { -+ int n_encapsulations; -+ int n_decapsulations; -+ int n_mh_in_msg; -+ int n_mh_in_error; -+ int n_mh_out_msg; -+ int n_mh_out_error; -+ -+ int n_brr_rcvd; -+ int n_hoti_rcvd; -+ int n_coti_rcvd; -+ int n_hot_rcvd; -+ int n_cot_rcvd; -+ int n_bu_rcvd; -+ int n_ba_rcvd; -+ int n_ban_rcvd; -+ int n_be_rcvd; -+ -+ int n_brr_sent; -+ int n_hoti_sent; -+ int n_coti_sent; -+ int n_hot_sent; -+ int n_cot_sent; -+ int n_bu_sent; -+ int n_ba_sent; -+ int n_ban_sent; -+ int n_be_sent; -+ -+ int n_ha_rcvd; -+ int n_ha_sent; -+ -+ struct mipv6_drop n_bu_drop; -+ struct mipv6_drop n_ba_drop; -+ struct mipv6_drop n_brr_drop; -+ struct mipv6_drop n_be_drop; -+ struct mipv6_drop n_ha_drop; -+}; -+ -+extern struct mipv6_statistics mipv6_stats; -+ -+#ifdef CONFIG_SMP -+/* atomic_t is max 24 bits long */ -+#define MIPV6_INC_STATS(X) atomic_inc((atomic_t *)&mipv6_stats.X); -+#else -+#define MIPV6_INC_STATS(X) mipv6_stats.X++; -+#endif -+ -+int mipv6_stats_init(void); -+void mipv6_stats_exit(void); -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel.h -@@ -0,0 +1,35 @@ -+/* -+ * MIPL Mobile IPv6 IP6-IP6 tunneling header file -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _TUNNEL_H -+#define _TUNNEL_H -+ -+#include <linux/in6.h> -+#include <linux/if_arp.h> -+#include <net/ipv6_tunnel.h> -+ -+static __inline__ int is_mip6_tnl(struct ip6_tnl *t) -+{ -+ return (t != NULL && -+ t->parms.flags & IP6_TNL_F_KERNEL_DEV && -+ t->parms.flags & IP6_TNL_F_MIP6_DEV); -+ -+} -+ -+static __inline__ int dev_is_mip6_tnl(struct net_device *dev) -+{ -+ struct ip6_tnl *t = (struct ip6_tnl *)dev->priv; -+ return (dev->type == ARPHRD_TUNNEL6 && is_mip6_tnl(t)); -+} -+ -+ -+#endif -+ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_ha.c -@@ -0,0 +1,264 @@ -+/* -+ * IPv6-IPv6 tunneling module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Ville Nuorvala <vnuorval@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/net.h> -+#include <linux/skbuff.h> -+#include <linux/ipv6.h> -+#include <linux/net.h> -+#include <linux/netdevice.h> -+#include <linux/init.h> -+#include <linux/route.h> -+#include <linux/ipv6_route.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/protocol.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/dst.h> -+#include <net/addrconf.h> -+ -+#include "tunnel.h" -+#include "debug.h" -+#include "stats.h" -+#include "config.h" -+ -+#define MIPV6_TNL_MAX IP6_TNL_MAX -+#define MIPV6_TNL_MIN 1 -+ -+int mipv6_max_tnls = 3; -+int mipv6_min_tnls = 1; -+ -+DECLARE_MUTEX(tnl_sem); -+ -+int mipv6_max_tnls_sysctl(ctl_table *ctl, int write, struct file *filp, -+ void *buffer, size_t *lenp) -+{ -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ down(&tnl_sem); -+ if (write) { -+ int diff; -+ int old_max_tnls = mipv6_max_tnls; -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ if (err < 0) -+ goto out; -+ if (mipv6_max_tnls < mipv6_min_tnls || -+ mipv6_max_tnls > MIPV6_TNL_MAX) { -+ mipv6_max_tnls = old_max_tnls; -+ goto out; -+ } -+ if (mipv6_max_tnls < old_max_tnls) { -+ diff = old_max_tnls - mipv6_max_tnls; -+ ip6ip6_tnl_dec_max_kdev_count(diff); -+ } else if (mipv6_max_tnls > old_max_tnls) { -+ diff = mipv6_max_tnls - old_max_tnls; -+ ip6ip6_tnl_inc_max_kdev_count(diff); -+ } -+ } else { -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ } -+out: -+ up(&tnl_sem); -+ return err; -+} -+ -+int mipv6_min_tnls_sysctl(ctl_table *ctl, int write, struct file *filp, -+ void *buffer, size_t *lenp) -+{ -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ down(&tnl_sem); -+ if (write) { -+ int diff; -+ int old_min_tnls = mipv6_min_tnls; -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ if (err < 0) -+ goto out; -+ if (mipv6_min_tnls > mipv6_max_tnls || -+ mipv6_min_tnls < MIPV6_TNL_MIN) { -+ mipv6_min_tnls = old_min_tnls; -+ goto out; -+ } -+ if (mipv6_min_tnls < old_min_tnls) { -+ diff = old_min_tnls - mipv6_min_tnls; -+ ip6ip6_tnl_dec_min_kdev_count(diff); -+ } else if (mipv6_min_tnls > old_min_tnls) { -+ diff = mipv6_min_tnls - old_min_tnls; -+ ip6ip6_tnl_inc_min_kdev_count(diff); -+ } -+ } else { -+ err = proc_dointvec(ctl, write, filp, buffer, lenp); -+ } -+out: -+ up(&tnl_sem); -+ return err; -+} -+ -+static __inline__ int mipv6_tnl_add(struct in6_addr *remote, -+ struct in6_addr *local) -+{ -+ struct ip6_tnl_parm p; -+ int ret; -+ -+ DEBUG_FUNC(); -+ -+ memset(&p, 0, sizeof(p)); -+ p.proto = IPPROTO_IPV6; -+ ipv6_addr_copy(&p.laddr, local); -+ ipv6_addr_copy(&p.raddr, remote); -+ p.hop_limit = 255; -+ p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | -+ IP6_TNL_F_IGN_ENCAP_LIMIT); -+ -+ ret = ip6ip6_kernel_tnl_add(&p); -+ if (ret > 0) { -+ DEBUG(DBG_INFO, "added tunnel from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(local), NIPV6ADDR(remote)); -+ } else { -+ DEBUG(DBG_WARNING, "unable to add tunnel from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(local), NIPV6ADDR(remote)); -+ } -+ return ret; -+} -+ -+static __inline__ int mipv6_tnl_del(struct in6_addr *remote, -+ struct in6_addr *local) -+{ -+ struct ip6_tnl *t = ip6ip6_tnl_lookup(remote, local); -+ -+ DEBUG_FUNC(); -+ -+ if (t != NULL && (t->parms.flags & IP6_TNL_F_MIP6_DEV)) { -+ DEBUG(DBG_INFO, "deleting tunnel from: " -+ "%x:%x:%x:%x:%x:%x:%x:%x to: %x:%x:%x:%x:%x:%x:%x:%x", -+ NIPV6ADDR(local), NIPV6ADDR(remote)); -+ -+ return ip6ip6_kernel_tnl_del(t); -+ } -+ return 0; -+} -+ -+static int add_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ struct in6_rtmsg rtmsg; -+ int err; -+ struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr); -+ -+ if (!is_mip6_tnl(t)) { -+ DEBUG(DBG_CRITICAL,"Tunnel missing"); -+ return -ENODEV; -+ } -+ -+ DEBUG(DBG_INFO, "adding route to: %x:%x:%x:%x:%x:%x:%x:%x via " -+ "tunnel device", NIPV6ADDR(home_addr)); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr); -+ rtmsg.rtmsg_dst_len = 128; -+ rtmsg.rtmsg_type = RTMSG_NEWROUTE; -+ rtmsg.rtmsg_flags = RTF_UP | RTF_NONEXTHOP | RTF_HOST | RTF_MOBILENODE; -+ rtmsg.rtmsg_ifindex = t->dev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) { -+ err = 0; -+ } -+ return err; -+} -+ -+static void del_route_to_mn(struct in6_addr *coa, struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ struct ip6_tnl *t = ip6ip6_tnl_lookup(coa, ha_addr); -+ -+ DEBUG_FUNC(); -+ -+ if (is_mip6_tnl(t)) { -+ struct in6_rtmsg rtmsg; -+ -+ DEBUG(DBG_INFO, "deleting route to: %x:%x:%x:%x:%x:%x:%x:%x " -+ " via tunnel device", NIPV6ADDR(home_addr)); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ ipv6_addr_copy(&rtmsg.rtmsg_dst, home_addr); -+ rtmsg.rtmsg_dst_len = 128; -+ rtmsg.rtmsg_ifindex = t->dev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ ip6_route_del(&rtmsg, NULL); -+ } -+} -+ -+ -+int mipv6_add_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ int ret; -+ -+ DEBUG_FUNC(); -+ -+ ret = mipv6_tnl_add(coa, ha_addr); -+ -+ if (ret > 0) { -+ int err = add_route_to_mn(coa, ha_addr, home_addr); -+ if (err) { -+ if (err != -ENODEV) { -+ mipv6_tnl_del(coa, ha_addr); -+ } -+ return err; -+ } -+ } -+ return ret; -+} -+ -+int mipv6_del_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr) -+{ -+ DEBUG_FUNC(); -+ del_route_to_mn(coa, ha_addr, home_addr); -+ return mipv6_tnl_del(coa, ha_addr); -+} -+ -+__init void mipv6_initialize_tunnel(void) -+{ -+ down(&tnl_sem); -+ ip6ip6_tnl_inc_max_kdev_count(mipv6_max_tnls); -+ ip6ip6_tnl_inc_min_kdev_count(mipv6_min_tnls); -+ up(&tnl_sem); -+ mip6_fn.bce_tnl_rt_add = add_route_to_mn; -+ mip6_fn.bce_tnl_rt_del = del_route_to_mn; -+} -+ -+__exit void mipv6_shutdown_tunnel(void) -+{ -+ mip6_fn.bce_tnl_rt_del = NULL; -+ mip6_fn.bce_tnl_rt_add = NULL; -+ down(&tnl_sem); -+ ip6ip6_tnl_dec_min_kdev_count(mipv6_min_tnls); -+ ip6ip6_tnl_dec_max_kdev_count(mipv6_max_tnls); -+ up(&tnl_sem); -+} -+ ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_ha.h -@@ -0,0 +1,20 @@ -+#ifndef _TUNNEL_HA_H -+#define _TUNNEL_HA_H -+ -+#include "tunnel.h" -+ -+extern int mipv6_max_tnls; -+extern int mipv6_min_tnls; -+ -+extern void mipv6_initialize_tunnel(void); -+extern void mipv6_shutdown_tunnel(void); -+ -+extern int mipv6_add_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+extern int mipv6_del_tnl_to_mn(struct in6_addr *coa, -+ struct in6_addr *ha_addr, -+ struct in6_addr *home_addr); -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_mn.c -@@ -0,0 +1,160 @@ -+/* -+ * IPv6-IPv6 tunneling module -+ * -+ * Authors: -+ * Sami Kivisaari <skivisaa@cc.hut.fi> -+ * Ville Nuorvala <vnuorval@tml.hut.fi> -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ * -+ */ -+ -+#include <linux/net.h> -+#include <linux/skbuff.h> -+#include <linux/ipv6.h> -+#include <linux/net.h> -+#include <linux/netdevice.h> -+#include <linux/init.h> -+#include <linux/route.h> -+#include <linux/ipv6_route.h> -+ -+#ifdef CONFIG_SYSCTL -+#include <linux/sysctl.h> -+#endif /* CONFIG_SYSCTL */ -+ -+#include <net/protocol.h> -+#include <net/ipv6.h> -+#include <net/ip6_route.h> -+#include <net/dst.h> -+#include <net/addrconf.h> -+ -+#include "tunnel.h" -+#include "debug.h" -+#include "stats.h" -+ -+static struct net_device *mn_ha_tdev; -+ -+static spinlock_t mn_ha_lock = SPIN_LOCK_UNLOCKED; -+ -+static __inline__ int add_reverse_route(struct in6_addr *ha_addr, -+ struct in6_addr *home_addr, -+ struct net_device *tdev) -+{ -+ struct in6_rtmsg rtmsg; -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ rtmsg.rtmsg_type = RTMSG_NEWROUTE; -+ ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr); -+ rtmsg.rtmsg_src_len = 128; -+ rtmsg.rtmsg_flags = RTF_UP | RTF_DEFAULT; -+ rtmsg.rtmsg_ifindex = tdev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ if ((err = ip6_route_add(&rtmsg, NULL)) == -EEXIST) { -+ return 0; -+ } -+ return err; -+} -+ -+static __inline__ void del_reverse_route(struct in6_addr *ha_addr, -+ struct in6_addr *home_addr, -+ struct net_device *tdev) -+{ -+ struct in6_rtmsg rtmsg; -+ -+ DEBUG(DBG_INFO, "removing reverse route via tunnel device"); -+ -+ memset(&rtmsg, 0, sizeof(rtmsg)); -+ ipv6_addr_copy(&rtmsg.rtmsg_src, home_addr); -+ rtmsg.rtmsg_src_len = 128; -+ rtmsg.rtmsg_ifindex = tdev->ifindex; -+ rtmsg.rtmsg_metric = IP6_RT_PRIO_MIPV6; -+ ip6_route_del(&rtmsg, NULL); -+} -+ -+int mipv6_add_tnl_to_ha(void) -+{ -+ struct ip6_tnl_parm p; -+ struct ip6_tnl *t; -+ int err; -+ -+ DEBUG_FUNC(); -+ -+ memset(&p, 0, sizeof(p)); -+ p.proto = IPPROTO_IPV6; -+ p.hop_limit = 255; -+ p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | -+ IP6_TNL_F_IGN_ENCAP_LIMIT); -+ strcpy(p.name, "mip6mnha1"); -+ -+ rtnl_lock(); -+ if ((err = ip6ip6_tnl_create(&p, &t))) { -+ rtnl_unlock(); -+ return err; -+ } -+ spin_lock_bh(&mn_ha_lock); -+ -+ if (!mn_ha_tdev) { -+ mn_ha_tdev = t->dev; -+ dev_hold(mn_ha_tdev); -+ } -+ spin_unlock_bh(&mn_ha_lock); -+ dev_open(t->dev); -+ rtnl_unlock(); -+ return 0; -+} -+ -+int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, -+ struct in6_addr *coa, -+ struct in6_addr *home_addr) -+{ -+ int err = -ENODEV; -+ -+ DEBUG_FUNC(); -+ -+ spin_lock_bh(&mn_ha_lock); -+ if (mn_ha_tdev) { -+ struct ip6_tnl_parm p; -+ memset(&p, 0, sizeof(p)); -+ p.proto = IPPROTO_IPV6; -+ ipv6_addr_copy(&p.laddr, coa); -+ ipv6_addr_copy(&p.raddr, ha_addr); -+ p.hop_limit = 255; -+ p.flags = (IP6_TNL_F_KERNEL_DEV | IP6_TNL_F_MIP6_DEV | -+ IP6_TNL_F_IGN_ENCAP_LIMIT); -+ -+ ip6ip6_tnl_change((struct ip6_tnl *) mn_ha_tdev->priv, &p); -+ if (ipv6_addr_cmp(coa, home_addr)) { -+ err = add_reverse_route(ha_addr, home_addr, -+ mn_ha_tdev); -+ } else { -+ del_reverse_route(ha_addr, home_addr, mn_ha_tdev); -+ err = 0; -+ } -+ } -+ spin_unlock_bh(&mn_ha_lock); -+ return err; -+} -+ -+void mipv6_del_tnl_to_ha(void) -+{ -+ struct net_device *dev; -+ -+ DEBUG_FUNC(); -+ -+ rtnl_lock(); -+ spin_lock_bh(&mn_ha_lock); -+ dev = mn_ha_tdev; -+ mn_ha_tdev = NULL; -+ spin_unlock_bh(&mn_ha_lock); -+ dev_put(dev); -+ unregister_netdevice(dev); -+ rtnl_unlock(); -+} ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/tunnel_mn.h -@@ -0,0 +1,14 @@ -+#ifndef _TUNNEL_MN_H -+#define _TUNNEL_MN_H -+ -+#include "tunnel.h" -+ -+extern int mipv6_add_tnl_to_ha(void); -+ -+extern int mipv6_mv_tnl_to_ha(struct in6_addr *ha_addr, -+ struct in6_addr *coa, -+ struct in6_addr *home_addr); -+ -+extern int mipv6_del_tnl_to_ha(void); -+ -+#endif ---- /dev/null -+++ linux-2.4.27/net/ipv6/mobile_ip6/util.h -@@ -0,0 +1,91 @@ -+/* -+ * MIPL Mobile IPv6 Utility functions -+ * -+ * $Id$ -+ * -+ * This program is free software; you can redistribute it and/or -+ * modify it under the terms of the GNU General Public License -+ * as published by the Free Software Foundation; either version -+ * 2 of the License, or (at your option) any later version. -+ */ -+ -+#ifndef _UTIL_H -+#define _UTIL_H -+ -+#include <linux/in6.h> -+#include <asm/byteorder.h> -+ -+/** -+ * mipv6_prefix_compare - Compare two IPv6 prefixes -+ * @addr: IPv6 address -+ * @prefix: IPv6 address -+ * @nprefix: number of bits to compare -+ * -+ * Perform prefix comparison bitwise for the @nprefix first bits -+ * Returns 1, if the prefixes are the same, 0 otherwise -+ **/ -+static inline int mipv6_prefix_compare(const struct in6_addr *addr, -+ const struct in6_addr *prefix, -+ const unsigned int pfix_len) -+{ -+ int i; -+ unsigned int nprefix = pfix_len; -+ -+ if (nprefix > 128) -+ return 0; -+ -+ for (i = 0; nprefix > 0; nprefix -= 32, i++) { -+ if (nprefix >= 32) { -+ if (addr->s6_addr32[i] != prefix->s6_addr32[i]) -+ return 0; -+ } else { -+ if (((addr->s6_addr32[i] ^ prefix->s6_addr32[i]) & -+ ((~0) << (32 - nprefix))) != 0) -+ return 0; -+ return 1; -+ } -+ } -+ -+ return 1; -+} -+ -+/** -+ * homeagent_anycast - Compute Home Agent anycast address -+ * @ac_addr: append home agent anycast suffix to passed prefix -+ * @prefix: prefix ha anycast address is generated from -+ * @plen: length of prefix in bits -+ * -+ * Calculate corresponding Home Agent Anycast Address (RFC2526) in a -+ * given subnet. -+ */ -+static inline int -+mipv6_ha_anycast(struct in6_addr *ac_addr, struct in6_addr *prefix, int plen) -+{ -+ if (plen <= 0 || plen > 120) { -+ /* error, interface id should be minimum 8 bits */ -+ return -1; -+ } -+ ipv6_addr_copy(ac_addr, prefix); -+ -+ if (plen < 32) -+ ac_addr->s6_addr32[0] |= htonl((u32)(~0) >> plen); -+ if (plen < 64) -+ ac_addr->s6_addr32[1] |= htonl((u32)(~0) >> (plen > 32 ? plen % 32 : 0)); -+ if (plen < 92) -+ ac_addr->s6_addr32[2] |= htonl((u32)(~0) >> (plen > 64 ? plen % 32 : 0)); -+ if (plen <= 120) -+ ac_addr->s6_addr32[3] |= htonl((u32)(~0) >> (plen > 92 ? plen % 32 : 0)); -+ -+ /* RFC2526: for interface identifiers in EUI-64 -+ * format, the universal/local bit in the interface -+ * identifier MUST be set to 0. */ -+ if (plen == 64) { -+ ac_addr->s6_addr32[2] &= (int)htonl(0xfdffffff); -+ } -+ /* Mobile IPv6 Home-Agents anycast id (0x7e) */ -+ ac_addr->s6_addr32[3] &= (int)htonl(0xfffffffe); -+ -+ return 0; -+} -+ -+#endif /* _UTIL_H */ ---- linux-2.4.27/net/ipv6/ndisc.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/ndisc.c -@@ -23,6 +23,7 @@ - * and moved to net/core. - * Pekka Savola : RFC2461 validation - * YOSHIFUJI Hideaki @USAGI : Verify ND options properly -+ * Ville Nuorvala : RFC2461 fixes to proxy ND - */ - - /* Set to 3 to get tracing... */ -@@ -70,6 +71,7 @@ - #include <net/ip6_route.h> - #include <net/addrconf.h> - #include <net/icmp.h> -+#include <net/mipglue.h> - - #include <net/checksum.h> - #include <linux/proc_fs.h> -@@ -187,6 +189,8 @@ - case ND_OPT_TARGET_LL_ADDR: - case ND_OPT_MTU: - case ND_OPT_REDIRECT_HDR: -+ case ND_OPT_RTR_ADV_INTERVAL: -+ case ND_OPT_HOME_AGENT_INFO: - if (ndopts->nd_opt_array[nd_opt->nd_opt_type]) { - ND_PRINTK2((KERN_WARNING - "ndisc_parse_options(): duplicated ND6 option found: type=%d\n", -@@ -372,8 +376,8 @@ - */ - - void ndisc_send_na(struct net_device *dev, struct neighbour *neigh, -- struct in6_addr *daddr, struct in6_addr *solicited_addr, -- int router, int solicited, int override, int inc_opt) -+ struct in6_addr *daddr, struct in6_addr *solicited_addr, -+ int router, int solicited, int override, int inc_opt) - { - static struct in6_addr tmpaddr; - struct inet6_ifaddr *ifp; -@@ -766,7 +770,8 @@ - int addr_type = ipv6_addr_type(saddr); - - if (in6_dev && in6_dev->cnf.forwarding && -- (addr_type & IPV6_ADDR_UNICAST) && -+ (addr_type & IPV6_ADDR_UNICAST || -+ addr_type == IPV6_ADDR_ANY) && - pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) { - int inc = ipv6_addr_type(daddr)&IPV6_ADDR_MULTICAST; - -@@ -778,13 +783,21 @@ - nd_tbl.stats.rcv_probes_mcast++; - else - nd_tbl.stats.rcv_probes_ucast++; -- -- neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); - -- if (neigh) { -- ndisc_send_na(dev, neigh, saddr, &msg->target, -- 0, 1, 0, 1); -- neigh_release(neigh); -+ if (addr_type & IPV6_ADDR_UNICAST) { -+ neigh = neigh_event_ns(&nd_tbl, lladdr, saddr, dev); -+ -+ if (neigh) { -+ ndisc_send_na(dev, neigh, saddr, &msg->target, -+ 0, 1, 0, 1); -+ neigh_release(neigh); -+ } -+ } else { -+ /* the proxy should also protect against DAD */ -+ struct in6_addr maddr; -+ ipv6_addr_all_nodes(&maddr); -+ ndisc_send_na(dev, NULL, &maddr, &msg->target, -+ 0, 0, 0, 1); - } - } else { - struct sk_buff *n = skb_clone(skb, GFP_ATOMIC); -@@ -849,6 +862,9 @@ - if (ifp->flags & IFA_F_TENTATIVE) { - addrconf_dad_failure(ifp); - return; -+ } else if (ndisc_mip_mn_ha_probe(ifp, lladdr)) { -+ in6_ifa_put(ifp); -+ return; - } - /* What should we make now? The advertisement - is invalid, but ndisc specs say nothing -@@ -887,6 +903,7 @@ - msg->icmph.icmp6_override, 1); - neigh_release(neigh); - } -+ ndisc_check_mipv6_dad(&msg->target); - } - - static void ndisc_router_discovery(struct sk_buff *skb) -@@ -894,6 +911,7 @@ - struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw; - struct neighbour *neigh; - struct inet6_dev *in6_dev; -+ int change_rtr; - struct rt6_info *rt; - int lifetime; - struct ndisc_options ndopts; -@@ -923,10 +941,6 @@ - ND_PRINTK1("RA: can't find in6 device\n"); - return; - } -- if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { -- in6_dev_put(in6_dev); -- return; -- } - - if (!ndisc_parse_options(opt, optlen, &ndopts)) { - in6_dev_put(in6_dev); -@@ -935,7 +949,12 @@ - "ICMP6 RA: invalid ND option, ignored.\n"); - return; - } -+ change_rtr = ndisc_mipv6_ra_rcv(skb, &ndopts); - -+ if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) { -+ in6_dev_put(in6_dev); -+ return; -+ } - if (in6_dev->if_flags & IF_RS_SENT) { - /* - * flag that an RA was received after an RS was sent -@@ -963,8 +982,7 @@ - ip6_del_rt(rt, NULL); - rt = NULL; - } -- -- if (rt == NULL && lifetime) { -+ if (rt == NULL && lifetime && change_rtr) { - ND_PRINTK2("ndisc_rdisc: adding default router\n"); - - rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev); -@@ -1087,6 +1105,8 @@ - if (rt) - dst_release(&rt->u.dst); - in6_dev_put(in6_dev); -+ -+ ndisc_mipv6_change_router(change_rtr); - } - - static void ndisc_redirect_rcv(struct sk_buff *skb) ---- linux-2.4.27/net/ipv6/raw.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/raw.c -@@ -43,6 +43,7 @@ - #include <net/transp_v6.h> - #include <net/udp.h> - #include <net/inet_common.h> -+#include <net/mipglue.h> - - #include <net/rawv6.h> - -@@ -641,6 +642,7 @@ - hdr.daddr = daddr; - else - hdr.daddr = NULL; -+ hdr.daddr = mipv6_get_fake_hdr_daddr(hdr.daddr, daddr); - - err = ip6_build_xmit(sk, rawv6_frag_cksum, &hdr, &fl, len, - opt, hlimit, msg->msg_flags); ---- linux-2.4.27/net/ipv6/route.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/route.c -@@ -49,6 +49,7 @@ - #include <net/addrconf.h> - #include <net/tcp.h> - #include <linux/rtnetlink.h> -+#include <net/mipglue.h> - - #include <asm/uaccess.h> - -@@ -363,12 +364,8 @@ - rt->u.dst.flags |= DST_HOST; - - #ifdef CONFIG_IPV6_SUBTREES -- if (rt->rt6i_src.plen && saddr) { -- ipv6_addr_copy(&rt->rt6i_src.addr, saddr); -- rt->rt6i_src.plen = 128; -- } -+ rt->rt6i_src.plen = ort->rt6i_src.plen; - #endif -- - rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); - - dst_hold(&rt->u.dst); -@@ -511,14 +508,19 @@ - struct rt6_info *rt; - int strict; - int attempts = 3; -+ struct in6_addr *saddr; - -+ if (ipv6_chk_addr(fl->nl_u.ip6_u.daddr, NULL)) -+ saddr = NULL; -+ else -+ saddr = fl->nl_u.ip6_u.saddr; -+ - strict = ipv6_addr_type(fl->nl_u.ip6_u.daddr) & (IPV6_ADDR_MULTICAST|IPV6_ADDR_LINKLOCAL); - - relookup: - read_lock_bh(&rt6_lock); - -- fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, -- fl->nl_u.ip6_u.saddr); -+ fn = fib6_lookup(&ip6_routing_table, fl->nl_u.ip6_u.daddr, saddr); - - restart: - rt = fn->leaf; -@@ -663,25 +665,6 @@ - return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size); - } - --/* Clean host part of a prefix. Not necessary in radix tree, -- but results in cleaner routing tables. -- -- Remove it only when all the things will work! -- */ -- --static void ipv6_addr_prefix(struct in6_addr *pfx, -- const struct in6_addr *addr, int plen) --{ -- int b = plen&0x7; -- int o = plen>>3; -- -- memcpy(pfx->s6_addr, addr, o); -- if (o < 16) -- memset(pfx->s6_addr + o, 0, 16 - o); -- if (b != 0) -- pfx->s6_addr[o] = addr->s6_addr[o]&(0xff00 >> b); --} -- - static int ipv6_get_mtu(struct net_device *dev) - { - int mtu = IPV6_MIN_MTU; -@@ -810,7 +793,7 @@ - if (!(gwa_type&IPV6_ADDR_UNICAST)) - goto out; - -- grt = rt6_lookup(gw_addr, NULL, rtmsg->rtmsg_ifindex, 1); -+ grt = rt6_lookup(gw_addr, &rtmsg->rtmsg_src, rtmsg->rtmsg_ifindex, 1); - - err = -EHOSTUNREACH; - if (grt == NULL) -@@ -848,7 +831,15 @@ - goto out; - } - } -- -+#ifdef USE_IPV6_MOBILITY -+ /* If destination is mobile node, add special skb->dst->input -+ * function for proxy ND. -+ */ -+ if (rtmsg->rtmsg_flags & RTF_MOBILENODE) { -+ rt->u.dst.input = ip6_mipv6_forward; -+ } -+#endif /* CONFIG_IPV6_MOBILITY */ -+ - if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) - rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; - else -@@ -936,7 +927,7 @@ - struct rt6_info *rt, *nrt; - - /* Locate old route to this destination. */ -- rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); -+ rt = rt6_lookup(dest, saddr, neigh->dev->ifindex, 1); - - if (rt == NULL) - return; -@@ -1003,6 +994,9 @@ - nrt = ip6_rt_copy(rt); - if (nrt == NULL) - goto out; -+#ifdef CONFIG_IPV6_SUBTREES -+ nrt->rt6i_src.plen = rt->rt6i_src.plen; -+#endif - - nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; - if (on_link) -@@ -1104,6 +1098,9 @@ - nrt = ip6_rt_copy(rt); - if (nrt == NULL) - goto out; -+#ifdef CONFIG_IPV6_SUBTREES -+ nrt->rt6i_src.plen = rt->rt6i_src.plen; -+#endif - ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); - nrt->rt6i_dst.plen = 128; - nrt->u.dst.flags |= DST_HOST; ---- linux-2.4.27/net/ipv6/tcp_ipv6.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/tcp_ipv6.c -@@ -50,6 +50,7 @@ - #include <net/addrconf.h> - #include <net/ip6_route.h> - #include <net/inet_ecn.h> -+#include <net/mipglue.h> - - #include <asm/uaccess.h> - -@@ -557,6 +558,7 @@ - struct flowi fl; - struct dst_entry *dst; - int addr_type; -+ int reroute = 0; - int err; - - if (addr_len < SIN6_LEN_RFC2133) -@@ -660,7 +662,7 @@ - - fl.proto = IPPROTO_TCP; - fl.fl6_dst = &np->daddr; -- fl.fl6_src = saddr; -+ fl.fl6_src = saddr; - fl.oif = sk->bound_dev_if; - fl.uli_u.ports.dport = usin->sin6_port; - fl.uli_u.ports.sport = sk->sport; -@@ -669,31 +671,46 @@ - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; - fl.nl_u.ip6_u.daddr = rt0->addr; - } -- - dst = ip6_route_output(sk, &fl); -- -+#ifdef CONFIG_IPV6_SUBTREES -+ reroute = (saddr == NULL); -+#endif - if ((err = dst->error) != 0) { - dst_release(dst); - goto failure; - } -- -- ip6_dst_store(sk, dst, NULL); -- sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -- -+ if (!reroute) { -+ ip6_dst_store(sk, dst, NULL, NULL); -+ sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -+ } - if (saddr == NULL) { - err = ipv6_get_saddr(dst, &np->daddr, &saddr_buf); -+ -+ if (reroute) -+ dst_release(dst); - if (err) - goto failure; - - saddr = &saddr_buf; -+ ipv6_addr_copy(&np->rcv_saddr, saddr); -+#ifdef CONFIG_IPV6_SUBTREES -+ fl.fl6_src = saddr; -+ dst = ip6_route_output(sk, &fl); -+ -+ if ((err = dst->error) != 0) { -+ dst_release(dst); -+ goto failure; -+ } -+ ip6_dst_store(sk, dst, NULL, NULL); -+ sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; -+#endif - } - - /* set the source address */ -- ipv6_addr_copy(&np->rcv_saddr, saddr); - ipv6_addr_copy(&np->saddr, saddr); - sk->rcv_saddr= LOOPBACK4_IPV6; - -- tp->ext_header_len = 0; -+ tp->ext_header_len = tcp_v6_get_mipv6_header_len(); - if (np->opt) - tp->ext_header_len = np->opt->opt_flen+np->opt->opt_nflen; - tp->mss_clamp = IPV6_MIN_MTU - sizeof(struct tcphdr) - sizeof(struct ipv6hdr); -@@ -1338,7 +1355,7 @@ - #endif - MOD_INC_USE_COUNT; - -- ip6_dst_store(newsk, dst, NULL); -+ ip6_dst_store(newsk, dst, NULL, NULL); - sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - - newtp = &(newsk->tp_pinfo.af_tcp); -@@ -1383,7 +1400,7 @@ - sock_kfree_s(sk, opt, opt->tot_len); - } - -- newtp->ext_header_len = 0; -+ newtp->ext_header_len = tcp_v6_get_mipv6_header_len(); - if (np->opt) - newtp->ext_header_len = np->opt->opt_nflen + np->opt->opt_flen; - -@@ -1710,7 +1727,7 @@ - return err; - } - -- ip6_dst_store(sk, dst, NULL); -+ ip6_dst_store(sk, dst, NULL, NULL); - sk->route_caps = dst->dev->features&~NETIF_F_IP_CSUM; - } - -@@ -1749,7 +1766,7 @@ - return -sk->err_soft; - } - -- ip6_dst_store(sk, dst, NULL); -+ ip6_dst_store(sk, dst, NULL, NULL); - } - - skb->dst = dst_clone(dst); ---- linux-2.4.27/net/ipv6/udp.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/ipv6/udp.c -@@ -48,6 +48,7 @@ - #include <net/ip.h> - #include <net/udp.h> - #include <net/inet_common.h> -+#include <net/mipglue.h> - - #include <net/checksum.h> - -@@ -232,6 +233,7 @@ - struct ip6_flowlabel *flowlabel = NULL; - int addr_type; - int err; -+ int reroute = 0; - - if (usin->sin6_family == AF_INET) { - if (__ipv6_only_sock(sk)) -@@ -331,7 +333,7 @@ - - fl.proto = IPPROTO_UDP; - fl.fl6_dst = &np->daddr; -- fl.fl6_src = &saddr; -+ fl.fl6_src = NULL; - fl.oif = sk->bound_dev_if; - fl.uli_u.ports.dport = sk->dport; - fl.uli_u.ports.sport = sk->sport; -@@ -348,29 +350,44 @@ - struct rt0_hdr *rt0 = (struct rt0_hdr *) np->opt->srcrt; - fl.fl6_dst = rt0->addr; - } -- - dst = ip6_route_output(sk, &fl); -- - if ((err = dst->error) != 0) { - dst_release(dst); - fl6_sock_release(flowlabel); -- return err; -- } -- -- ip6_dst_store(sk, dst, fl.fl6_dst); -- -+ return err; -+ } -+#ifdef CONFIG_IPV6_SUBTREES -+ reroute = (fl.fl6_src == NULL); -+#endif - /* get the source adddress used in the apropriate device */ - - err = ipv6_get_saddr(dst, daddr, &saddr); - -+ if (reroute) -+ dst_release(dst); -+ - if (err == 0) { -- if(ipv6_addr_any(&np->saddr)) -+#ifdef CONFIG_IPV6_SUBTREES -+ if (reroute) { -+ fl.fl6_src = &saddr; -+ dst = ip6_route_output(sk, &fl); -+ if ((err = dst->error) != 0) { -+ dst_release(dst); -+ fl6_sock_release(flowlabel); -+ return err; -+ } -+ } -+#endif -+ if(ipv6_addr_any(&np->saddr)) { - ipv6_addr_copy(&np->saddr, &saddr); -- -+ fl.fl6_src = &np->saddr; -+ } - if(ipv6_addr_any(&np->rcv_saddr)) { - ipv6_addr_copy(&np->rcv_saddr, &saddr); - sk->rcv_saddr = LOOPBACK4_IPV6; - } -+ ip6_dst_store(sk, dst, fl.fl6_dst, -+ fl.fl6_src == &np->saddr ? fl.fl6_src : NULL); - sk->state = TCP_ESTABLISHED; - } - fl6_sock_release(flowlabel); -@@ -889,6 +906,7 @@ - opt = fl6_merge_options(&opt_space, flowlabel, opt); - if (opt && opt->srcrt) - udh.daddr = daddr; -+ udh.daddr = mipv6_get_fake_hdr_daddr(udh.daddr, daddr); - - udh.uh.source = sk->sport; - udh.uh.len = len < 0x10000 ? htons(len) : 0; ---- linux-2.4.27/net/netsyms.c~mipv6-1.1-v2.4.26 -+++ linux-2.4.27/net/netsyms.c -@@ -190,6 +190,7 @@ - #endif - EXPORT_SYMBOL(pneigh_lookup); - EXPORT_SYMBOL(pneigh_enqueue); -+EXPORT_SYMBOL(pneigh_delete); - EXPORT_SYMBOL(neigh_destroy); - EXPORT_SYMBOL(neigh_parms_alloc); - EXPORT_SYMBOL(neigh_parms_release); diff --git a/packages/linux/files/usb-gadget-ether-compat.patch b/packages/linux/files/usb-gadget-ether-compat.patch deleted file mode 100644 index 9e336cfa3c..0000000000 --- a/packages/linux/files/usb-gadget-ether-compat.patch +++ /dev/null @@ -1,30 +0,0 @@ ---- kernel/drivers/usb/gadget/ether.c 2005-04-24 12:40:08.867411535 +0200 -+++ /tmp/ether.c 2005-04-24 12:39:02.119093498 +0200 -@@ -231,6 +231,16 @@ - MODULE_PARM(host_addr, "s"); - MODULE_PARM_DESC(host_addr, "Host Ethernet Address"); - -+#ifdef CONFIG_USB_ETH_RNDIS -+/* setting it to 1 disables the RNDIS extension, -+ * needed to make g_ether compatible with usbnet from kernel < 2.6.10: -+ * - simple vendor/product -+ * - just the CDC interface -+ */ -+static u8 __initdata compat_mode; -+MODULE_PARM(compat_mode, "b"); -+MODULE_PARM_DESC(compat_mode, "non-zero value reverts to traditional usbnet compatibility (RNDIS disabled)"); -+#endif - - /*-------------------------------------------------------------------------*/ - -@@ -2336,6 +2346,10 @@ - #endif - #ifndef CONFIG_USB_ETH_RNDIS - rndis = 0; -+#else -+ if (compat_mode) { -+ rndis = 0; -+ } - #endif - - /* Because most host side USB stacks handle CDC Ethernet, that |
