diff -ur --new-file old/linux/include/linux/atmmpc.h new/linux/include/linux/atmmpc.h
--- old/linux/include/linux/atmmpc.h	Wed Aug 19 20:46:45 1998
+++ new/linux/include/linux/atmmpc.h	Wed Aug 19 20:47:28 1998
@@ -22,7 +22,6 @@
         uint8_t   eg_MPC_ATM_addr[ATM_ESA_LEN];
         uint32_t  tag;
         uint32_t  in_dst_ip;      /* IP address this ingress MPC sends packets to */
-        uint32_t  service_category;
         uint16_t  holding_time;
         uint32_t  request_id;
 } in_ctrl_info;
@@ -45,6 +44,7 @@
                 in_ctrl_info in_info;
                 eg_ctrl_info eg_info;
         } content;
+        struct atm_qos qos;            /* only used with content.in_info */
 } k_message;
 
 struct llc_snap_hdr { /* RFC 1483 LLC/SNAP encapsulation for routed IP PDUs */
diff -ur --new-file old/linux/net/atm/mpc.c new/linux/net/atm/mpc.c
--- old/linux/net/atm/mpc.c	Wed Aug 19 20:46:46 1998
+++ new/linux/net/atm/mpc.c	Wed Aug 19 20:47:29 1998
@@ -100,6 +100,7 @@
 #endif
 
 struct mpoa_client *mpcs = NULL; /* FIXME */
+static struct atm_mpoa_qos *qos_head = NULL;
 static struct timer_list mpc_timer;
 
 
@@ -145,6 +146,100 @@
         return NULL;   /* not found */
 }
 
+/*
+ * Functions for managing QoS list
+ */
+
+/*
+ * Returns the new entry. Note the use of the 2nd argument
+ */
+struct atm_mpoa_qos *atm_mpoa_add_qos(uint32_t dst_ip, struct atm_qos *qos)
+{
+        struct atm_mpoa_qos *entry;
+
+        entry = kmalloc(sizeof(struct atm_qos), GFP_KERNEL);
+        if (entry == NULL) {
+                printk("mpoa: atm_mpoa_add_qos: out of memory\n");
+                return entry;
+        }
+
+        entry->ipaddr = dst_ip;
+        entry->qos = *qos;
+
+        entry->next = qos_head;
+        qos_head = entry;
+
+        return entry;
+}
+
+struct atm_mpoa_qos *atm_mpoa_search_qos(uint32_t dst_ip)
+{
+        struct atm_mpoa_qos *qos;
+
+        qos = qos_head;
+        while( qos != NULL ){
+                if(qos->ipaddr == dst_ip) {
+                        break;
+		}
+                qos = qos->next;
+        }
+
+        return qos;
+}        
+
+/*
+ * Returns 0 for failure
+ */
+int atm_mpoa_delete_qos(struct atm_mpoa_qos *entry)
+{
+
+        struct atm_mpoa_qos *curr;
+
+        if (entry == NULL) return 0;
+        if (entry == qos_head) {
+                qos_head = qos_head->next;
+                kfree(qos_head);
+                return 1;
+        }
+
+        curr = qos_head;
+        while (curr != NULL) {
+                if (curr->next == entry) {
+                        curr->next = entry->next;
+                        kfree(entry);
+                        return 1;
+                }
+                curr = curr->next;
+        }
+
+        return 0;
+}
+
+void atm_mpoa_disp_qos(char *page, int *len)
+{
+
+	unsigned char *ip;
+	char ipaddr[16];
+	struct atm_mpoa_qos *qos;
+
+	qos = qos_head;
+	*len += sprintf(page + *len, "QoS entries for shortcuts:\n");
+	*len += sprintf(page + *len, "IP address     TX:max_pcr pcr     min_pcr max_cdv max_sdu  RX:max_pcr pcr     min_pcr max_cdv max_sdu\n");
+
+	ipaddr[sizeof(ipaddr)-1] = '\0';
+	while (qos != NULL) {
+		ip = (unsigned char *)&qos->ipaddr;
+		sprintf(ipaddr, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+		*len += sprintf(page + *len, "%-16s  %-7d %-7d %-7d %-7d %-7d     %-7d %-7d %-7d %-7d %-7d\n",
+				ipaddr,
+				qos->qos.txtp.max_pcr, qos->qos.txtp.pcr, qos->qos.txtp.min_pcr, qos->qos.txtp.max_cdv, qos->qos.txtp.max_sdu,
+				qos->qos.rxtp.max_pcr, qos->qos.rxtp.pcr, qos->qos.rxtp.min_pcr, qos->qos.rxtp.max_cdv, qos->qos.rxtp.max_sdu);
+		qos = qos->next;
+	}
+	
+	return;
+}
+
 static struct device *find_lec_by_itfnum(int itf)
 {
         extern struct atm_lane_ops atm_lane_ops; /* in common.c */
@@ -163,7 +258,7 @@
         if (mpc == NULL)
                 return NULL;
         memset(mpc, 0, sizeof(struct mpoa_client));
-#if 0
+#if 0 /* compiler seems to barf on this */
         mpc->ingress_lock = RW_LOCK_UNLOCKED;
         mpc->egress_lock  = RW_LOCK_UNLOCKED;
 #endif
@@ -597,6 +692,7 @@
                 return;
         }
 
+	eg->latest_ip_addr = ((struct iphdr *)new_skb->data)->saddr;
         eg->packets_rcvd++;
                 
         netif_rx(new_skb);
@@ -922,15 +1018,20 @@
 
 static void MPOA_res_reply_rcvd(struct k_message *msg, struct mpoa_client *client)
 {
+	unsigned char *ip;
+        struct atm_mpoa_qos *qos;
         uint32_t dst_ip = msg->content.in_info.in_dst_ip;
         in_cache_entry *entry = client->in_ops->search(dst_ip, client);
+	eg_cache_entry *eg_entry;
 
-        dprintk("mpoa: (%s) MPOA_res_reply_rcvd() entry = %p", client->dev->name, entry);
+	ip = (unsigned char *)&dst_ip;
+        dprintk("mpoa: (%s) MPOA_res_reply_rcvd: ip %d.%d.%d.%d\n", client->dev->name, ip[0], ip[1], ip[2], ip[3]);
+        ddprintk("mpoa: (%s) MPOA_res_reply_rcvd() entry = %p", client->dev->name, entry);
         if(entry == NULL){
                 printk("\nmpoa: (%s): ARGH, received res. reply for an entry that doesn't exist.\n", client->dev->name);
                 return;
         }
-        printk(" entry_state = %d ", entry->entry_state);	
+        ddprintk(" entry_state = %d ", entry->entry_state);	
 
         if (entry->entry_state == INGRESS_RESOLVED) {
                 printk("\nmpoa: (%s) MPOA_res_reply_rcvd for RESOLVED entry!\n", client->dev->name);
@@ -941,21 +1042,47 @@
         do_gettimeofday(&(entry->tv));
         do_gettimeofday(&(entry->reply_wait)); /* Used in refreshing func from now on */
         entry->refresh_time = 0;
-        printk("entry->shortcut = %p\n", entry->shortcut);
+        ddprintk("entry->shortcut = %p\n", entry->shortcut);
 
 	if(entry->entry_state == INGRESS_RESOLVING && entry->shortcut != NULL){
 	        entry->entry_state = INGRESS_RESOLVED; 
-	     return; /* Shortcut already open... */
+		return; /* Shortcut already open... */
 	}
-        if (entry->shortcut == NULL) {
-        	entry->entry_state = INGRESS_RESOLVED;
-	        msg->type = OPEN_INGRESS_SVC;
-	        msg_to_mpoad(msg,client);
-	        return;
-        }
 
-        printk("mpoa: (%s) MPOA_res_reply_rcvd: still here, impossible!\n", client->dev->name);
-        return;
+	if (entry->shortcut != NULL) {
+		printk("mpoa: (%s) MPOA_res_reply_rcvd: entry->shortcut != NULL, impossible!\n",
+		       client->dev->name);
+		return;
+	}
+
+	/* entry->shortcut == NULL so we need to get a VC. First check for candicates
+	 * in egress cache. If none found, ask daemon to create one.
+	 */
+	eg_entry = client->eg_ops->search_by_src_ip(dst_ip, client);
+	if (eg_entry != NULL) {
+		ip = (unsigned char *)&dst_ip;
+		entry->shortcut = eg_entry->shortcut;
+		dprintk("mpoa: (%s) using egress SVC to reach %d.%d.%d.%d\n",
+			client->dev->name, ip[0], ip[1], ip[2], ip[3]);
+
+	}
+
+	/* It is possible that there was an egress entry with no valid shortcut */
+	if (entry->shortcut == NULL) {
+		msg->type = OPEN_INGRESS_SVC;
+                qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
+                if (qos != NULL) {
+                        msg->qos = qos->qos;
+                        printk("mpoa: (%s) MPOA_res_reply_rcvd: trying to get a CBR shortcut\n",
+                               client->dev->name);
+                }
+		msg_to_mpoad(msg, client);
+	}
+
+	entry->entry_state = INGRESS_RESOLVED;
+
+	return;
+
 }
 
 static void ingress_purge_rcvd(struct k_message *msg, struct mpoa_client *mpc)
@@ -1226,6 +1353,7 @@
 {
         extern struct atm_mpoa_ops atm_mpoa_ops;
         struct mpoa_client *mpc, *tmp;
+        struct atm_mpoa_qos *qos, *nextqos;
         struct lec_priv *priv;
 
         if (MOD_IN_USE) {
@@ -1259,9 +1387,17 @@
                 memset(mpc, 0, sizeof(struct mpoa_client));
                 ddprintk("mpoa: cleanup_module: about to kfree %p\n", mpc);
                 kfree(mpc);
-                ddprintk("mpoa: cleanup_module: kfree() done\n");
                 ddprintk("mpoa: cleanup_module: next mpc is at %p\n", tmp);
                 mpc = tmp;
+        }
+
+        qos = qos_head;
+        qos_head = NULL;
+        while (qos != NULL) {
+                nextqos = qos->next;
+                dprintk("mpoa: cleanup_module: freeing qos entry %p\n", qos);
+                kfree(qos);
+                qos = nextqos;
         }
 
         return;
diff -ur --new-file old/linux/net/atm/mpc.h new/linux/net/atm/mpc.h
--- old/linux/net/atm/mpc.h	Wed Aug 19 20:46:46 1998
+++ new/linux/net/atm/mpc.h	Wed Aug 19 20:47:29 1998
@@ -1,6 +1,7 @@
 #ifndef _MPC_H_
 #define _MPC_H_
 
+#include <linux/atm.h>
 #include <linux/atmmpc.h>
 #include <linux/skbuff.h>
 #include <asm/spinlock.h>
@@ -16,23 +17,29 @@
 
 struct mpoa_client {
         struct mpoa_client *next;
-        struct device *dev;          /* lec in question                   */
-        int dev_num;                 /* e.g. 2 for lec2                   */
+        struct device *dev;          /* lec in question                     */
+        int dev_num;                 /* e.g. 2 for lec2                     */
         int (*old_hard_start_xmit)(struct sk_buff *skb, struct device *dev);
-        struct atm_vcc *mpoad_vcc;   /* control channel to mpoad          */
-        uint8_t mps_ctrl_addr[ATM_ESA_LEN];  /* MPS control ATM address   */
-        uint8_t our_ctrl_addr[ATM_ESA_LEN];  /* MPC's control ATM address */
+        struct atm_vcc *mpoad_vcc;   /* control channel to mpoad            */
+        uint8_t mps_ctrl_addr[ATM_ESA_LEN];  /* MPS control ATM address     */
+        uint8_t our_ctrl_addr[ATM_ESA_LEN];  /* MPC's control ATM address   */
 
         rwlock_t ingress_lock;
-        struct in_cache_ops *in_ops; /* ingress cache operations          */
-        in_cache_entry *in_cache;    /* the ingress cache of this MPC     */
+        struct in_cache_ops *in_ops; /* ingress cache operations            */
+        in_cache_entry *in_cache;    /* the ingress cache of this MPC       */
 
         rwlock_t egress_lock;
-        struct eg_cache_ops *eg_ops; /* egress cache operations           */
-        eg_cache_entry *eg_cache;    /* the egress  cache of this MPC     */
+        struct eg_cache_ops *eg_ops; /* egress cache operations             */
+        eg_cache_entry *eg_cache;    /* the egress  cache of this MPC       */
 
-        uint8_t *mps_macs;           /* array of MPS MAC addresses, >=1   */
-        int number_of_mps_macs;      /* number of the above MAC addresses */
+        uint8_t *mps_macs;           /* array of MPS MAC addresses, >=1     */
+        int number_of_mps_macs;      /* number of the above MAC addresses   */
+};
+
+struct atm_mpoa_qos {
+        struct atm_mpoa_qos *next;
+        uint32_t ipaddr;
+        struct atm_qos qos;
 };
 
 /* TLVs this MPC recognizes */
@@ -55,5 +62,12 @@
 void atm_mpoa_init(void);
 void atm_mpoa_init_ops(struct atm_mpoa_ops *ops);
 
-#endif /* _MPC_H_ */
+/* MPOA QoS operations */
+struct atm_mpoa_qos *atm_mpoa_add_qos(uint32_t dst_ip, struct atm_qos *qos);
+struct atm_mpoa_qos *atm_mpoa_search_qos(uint32_t dst_ip);
+int atm_mpoa_delete_qos(struct atm_mpoa_qos *qos);
 
+/* Display QoS entries. This is for the procfs */
+void atm_mpoa_disp_qos(char *page, int *len);
+
+#endif /* _MPC_H_ */
diff -ur --new-file old/linux/net/atm/mpoa_caches.c new/linux/net/atm/mpoa_caches.c
--- old/linux/net/atm/mpoa_caches.c	Wed Aug 19 20:46:46 1998
+++ new/linux/net/atm/mpoa_caches.c	Wed Aug 19 20:47:29 1998
@@ -100,6 +100,7 @@
 
 static int cache_hit( in_cache_entry * entry, struct mpoa_client *mpc)
 {
+        struct atm_mpoa_qos *qos;
         struct k_message msg;
 
         entry->count++;
@@ -111,6 +112,8 @@
 		        msg.type = SND_MPOA_RES_RTRY;
 			msg.content.in_info = entry->ctrl_info;
 			memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN);
+                        qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
+                        if (qos != NULL) msg.qos = qos->qos;
 			msg_to_mpoad(&msg, mpc);
 			do_gettimeofday(&(entry->reply_wait));
 			entry->entry_state = INGRESS_RESOLVING;
@@ -132,6 +135,8 @@
                 msg.type =  SND_MPOA_RES_RQST;
                 memcpy(msg.MPS_ctrl, mpc->mps_ctrl_addr, ATM_ESA_LEN );
                 msg.content.in_info = entry->ctrl_info;
+                qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
+                if (qos != NULL) msg.qos = qos->qos;
                 msg_to_mpoad( &msg, mpc);
                 do_gettimeofday(&(entry->reply_wait));
         }
@@ -228,6 +233,7 @@
 static void check_resolving_entries( struct mpoa_client * client )
 {
 
+        struct atm_mpoa_qos *qos;
         unsigned long flags;
         in_cache_entry *entry;
         struct timeval now;
@@ -258,7 +264,9 @@
                                 msg.type = SND_MPOA_RES_RTRY;
                                 memcpy(msg.MPS_ctrl, client->mps_ctrl_addr, ATM_ESA_LEN);
                                 msg.content.in_info = entry->ctrl_info;
-                                msg_to_mpoad(&msg, client );
+                                qos = atm_mpoa_search_qos(entry->ctrl_info.in_dst_ip);
+                                if (qos != NULL) msg.qos = qos->qos;
+                                msg_to_mpoad(&msg, client);
                                 do_gettimeofday(&(entry->reply_wait));
                         }
                 }
@@ -354,6 +362,25 @@
         return NULL;
 }
 
+static eg_cache_entry *eg_cache_search_by_src_ip(uint32_t ipaddr,
+						 struct mpoa_client *client)
+{
+        unsigned long flags;
+        eg_cache_entry *entry;
+
+        read_lock_irqsave(&client->egress_lock, flags);
+        entry = client->eg_cache;
+        while( entry != NULL ){
+                if(entry->latest_ip_addr == ipaddr) {
+                        break;
+		}
+                entry = entry->next;
+        }
+	read_unlock_irqrestore(&client->egress_lock, flags);
+
+        return entry;
+}
+
 /*
  * If there are no more references to vcc in ingress cache,
  * we are ready to close it.
@@ -464,7 +491,7 @@
 		        msg.type = SND_EGRESS_PURGE;
 			msg.content.eg_info = entry->ctrl_info;
 		        printk("mpoa: mpoa_caches.c: egress_cache:holding time expired, cache_id = %lu.\n",ntohl(entry->ctrl_info.cache_id));
-			msg_to_mpoad(&msg,client);
+			msg_to_mpoad(&msg, client);
                         eg_cache_remove(entry, client);
                 }
                 entry = next_entry;
@@ -484,7 +511,7 @@
         in_cache_remove,                  /* cache_remove    */
         clear_count_and_expired,          /* clear_count     */
         check_resolving_entries,          /* check_resolving */
-        refresh_entries                   /* refresh         */
+        refresh_entries,                  /* refresh         */
 };
 
 static struct eg_cache_ops egress_ops = {
@@ -492,6 +519,7 @@
         eg_cache_search_by_cache_id,      /* search_by_cache_id */
         eg_cache_search_by_tag,           /* search_by_tag      */ 
         eg_cache_search_by_vcc,           /* search_by_vcc      */
+        eg_cache_search_by_src_ip,        /* search_by_src_ip   */
         eg_cache_remove,                  /* cache_remove       */
         update_eg_cache_entry,            /* update             */
 	clear_expired                     /* clear_expired      */
diff -ur --new-file old/linux/net/atm/mpoa_caches.h new/linux/net/atm/mpoa_caches.h
--- old/linux/net/atm/mpoa_caches.h	Wed Aug 19 20:46:46 1998
+++ new/linux/net/atm/mpoa_caches.h	Wed Aug 19 20:47:29 1998
@@ -7,8 +7,6 @@
 #include <linux/atmdev.h>
 #include <linux/atmmpc.h>
 
-
-
 struct mpoa_client;
 
 void atm_mpoa_init_cache(struct mpoa_client *mpc);
@@ -52,6 +50,7 @@
         struct atm_vcc       *shortcut;
         uint32_t             packets_rcvd;
         uint16_t             entry_state;
+        uint32_t             latest_ip_addr;    /* The src IP address of the last packet */
         struct eg_ctrl_info  ctrl_info;
 } eg_cache_entry;
 
@@ -60,6 +59,7 @@
         eg_cache_entry *(*search_by_cache_id)(uint32_t cache_id, struct mpoa_client *client);
         eg_cache_entry *(*search_by_tag)(uint32_t cache_id, struct mpoa_client *client);
         eg_cache_entry *(*search_by_vcc)(struct atm_vcc *vcc, struct mpoa_client *client);
+        eg_cache_entry *(*search_by_src_ip)(uint32_t ipaddr, struct mpoa_client *client);
         int            (*cache_remove)(eg_cache_entry *entry, struct mpoa_client *client);
         void           (*update)(eg_cache_entry *entry, uint16_t holding_time);
         void           (*clear_expired)(struct mpoa_client *client);
diff -ur --new-file old/linux/net/atm/mpoa_proc.c new/linux/net/atm/mpoa_proc.c
--- old/linux/net/atm/mpoa_proc.c	Wed Aug 19 20:46:46 1998
+++ new/linux/net/atm/mpoa_proc.c	Wed Aug 19 20:47:29 1998
@@ -9,6 +9,7 @@
 #include <linux/time.h>
 #include <asm/uaccess.h>
 #include <linux/atmmpc.h>
+#include <linux/atm.h>
 #include "mpc.h"
 #include "mpoa_caches.h"
 
@@ -17,6 +18,12 @@
  * file system statistics 
  */
 
+#if 1
+#define dprintk printk   /* debug */
+#else
+#define dprintk(format,args...)
+#endif
+
 #define STAT_FILE_NAME "mpc"     /* Our statistic file's name */
 
 extern struct mpoa_client *mpcs;
@@ -25,13 +32,18 @@
 static ssize_t proc_mpc_read(struct file *file, char *buff,
 			     size_t count, loff_t *pos);
 
+static ssize_t proc_mpc_write(struct file *file, const char *buff,
+                              size_t nbytes, loff_t *ppos);
+
+static int parse_qos(const char *buff, int len);
+
 /*
  *   Define allowed FILE OPERATIONS
  */
 static struct file_operations mpc_file_operations = {
         NULL,                   /* lseek */
         proc_mpc_read,          /* read */
-        NULL,                   /* write */
+        proc_mpc_write,         /* write */
         NULL,                   /* readdir */
         NULL,                   /* poll - default */
         NULL,                   /* ioctl - default */
@@ -150,7 +162,8 @@
 	        return 0;
 	page = get_free_page(GFP_KERNEL);
 	if(!page)
-	        return -ENOMEM; 
+	        return -ENOMEM;
+	atm_mpoa_disp_qos((char *)page, &length);
 	while(mpc != NULL){
 	        length += print_header((char *)page + length, mpc);
 		length += sprintf((char *)page + length,"Ingress Entries:\nIP-address      State      Holding_time  Packets_fwded  VPI  VCI\n");
@@ -166,13 +179,19 @@
 		}
 		length += sprintf((char *)page + length,"\n");
 		eg_entry = mpc->eg_cache;
-		length += sprintf((char *)page + length,"Egress Entries:\nIngress_MPC_ATM_addr\nCache_id        State      Holding_time  Packets_recvd  VPI  VCI\n");
+		length += sprintf((char *)page + length,"Egress Entries:\nIngress_MPC_ATM_addr\nCache_id        State      Holding_time  Packets_recvd  Latest IP addr   VPI  VCI\n");
 		while(eg_entry != NULL){
 		  for(i=0;i<ATM_ESA_LEN;i++){
 		          length += sprintf((char *)page + length,"%02x",eg_entry->ctrl_info.in_MPC_data_ATM_addr[i]);}  
-		        length += sprintf((char *)page + length,"\n%-16lu%s%-14lu%-12u",ntohl(eg_entry->ctrl_info.cache_id), egress_state_string(eg_entry->entry_state), (eg_entry->ctrl_info.holding_time-(now.tv_sec-eg_entry->tv.tv_sec)), eg_entry->packets_rcvd);
+		        length += sprintf((char *)page + length,"\n%-16lu%s%-14lu%-15u",ntohl(eg_entry->ctrl_info.cache_id), egress_state_string(eg_entry->entry_state), (eg_entry->ctrl_info.holding_time-(now.tv_sec-eg_entry->tv.tv_sec)), eg_entry->packets_rcvd);
+			
+			/* latest IP address */
+			temp = (unsigned char *)&eg_entry->latest_ip_addr;
+			sprintf(ip_string, "%d.%d.%d.%d", temp[0], temp[1], temp[2], temp[3]);
+			length += sprintf((char *)page + length, "%-16s", ip_string);
+
 			if(eg_entry->shortcut)
-			        length += sprintf((char *)page + length,"   %-3d  %-3d",eg_entry->shortcut->vpi,eg_entry->shortcut->vci);
+			        length += sprintf((char *)page + length," %-3d  %-3d",eg_entry->shortcut->vpi,eg_entry->shortcut->vci);
 			length += sprintf((char *)page + length,"\n");
 			eg_entry = eg_entry->next;
 		}
@@ -189,6 +208,157 @@
 
  	free_page(page);
         return length;
+}
+
+static ssize_t proc_mpc_write(struct file *file, const char *buff,
+                              size_t nbytes, loff_t *ppos)
+{
+        int incoming, error, retval;
+        char *page, c;
+        const char *tmp;
+
+        if (nbytes < 0) return -EINVAL;
+        if (nbytes == 0) return 0;
+        if (nbytes > PAGE_SIZE) nbytes = PAGE_SIZE-1;
+
+        error = verify_area(VERIFY_READ, buff, nbytes);
+        if (error) return error;
+
+        page = (char *)__get_free_page(GFP_KERNEL);
+        if (page == NULL) return -ENOMEM;
+
+        incoming = 0;
+        tmp = buff;
+        while(incoming < nbytes){
+                if (get_user(c, tmp++)) return -EFAULT;
+                incoming++;
+                if (c == '\0' || c == '\n')
+                        break;
+        }
+
+        retval = copy_from_user(page, buff, incoming);
+        if (retval != 0) {
+                printk("mpoa: proc_mpc_write: copy_from_user() failed\n");
+                return -EFAULT;
+        }
+
+        *ppos += incoming;
+
+        page[incoming] = '\0';
+	retval = parse_qos(buff, incoming);
+        if (retval == 0)
+                printk("mpoa: proc_mpc_write: could not parse '%s'\n", page);
+
+        free_page((unsigned long)page);
+        
+        return nbytes;
+}
+
+static int parse_qos(const char *buff, int len)
+{
+        /* possible lines look like this
+         * add 130.230.54.142 tx=max_pcr,pcr,min_pcr,max_cdv,max_sdu rx=max_pcr,pcr,min_pcr,max_cdv,max_sd
+         */
+        
+        int pos, i;
+        uint32_t ipaddr;
+        unsigned char ip[4]; 
+        char cmd[4], temp[256];
+        const char *tmp, *prev;
+	struct atm_qos qos; 
+	int value[5];
+        
+	pos = 0;
+        strncpy(cmd, buff, 3);
+	if( strncmp(cmd,"add", 3) &&  strncmp(cmd,"del", 3))
+	        return 0;  /* not add or del */
+
+	pos += 4;
+        /* next parse ip */
+        prev = buff + pos;
+        for (i = 0; i < 3; i++) {
+                tmp = strchr(prev, '.');
+                if (tmp == NULL) return 0;
+                memset(temp, '\0', 256);
+                memcpy(temp, prev, tmp-prev);
+                ip[i] = (char)simple_strtoul(temp, NULL, 0);
+		tmp ++; 
+		prev = tmp;
+        }
+	tmp = strchr(prev, ' ');
+        if (tmp == NULL) return 0;
+        memset(temp, '\0', 256);
+        memcpy(temp, prev, tmp-prev);
+        ip[i] = (char)simple_strtoul(temp, NULL, 0);
+        ipaddr = *(uint32_t *)ip;
+                
+	if(!strncmp(cmd, "del", 3))
+	         return atm_mpoa_delete_qos(atm_mpoa_search_qos(ipaddr));
+	tmp = strstr(buff, "tx=");
+	if(tmp == NULL) return 0;
+	tmp += 3;
+	prev = tmp;
+	for( i = 0; i < 4; i++){
+	         tmp = strchr(prev, ',');
+		 if (tmp == NULL) return 0;
+		 memset(temp, '\0', 256);
+		 memcpy(temp, prev, tmp-prev);
+		 value[i] = (int)simple_strtoul(temp, NULL, 0);
+		 tmp ++; 
+		 prev = tmp;
+	}
+	tmp = strchr(prev, ' ');
+        if (tmp == NULL) return 0;
+	memset(temp, '\0', 256);
+        memcpy(temp, prev, tmp-prev);
+        value[i] = (int)simple_strtoul(temp, NULL, 0);
+	qos.txtp.traffic_class = ATM_CBR;
+	qos.txtp.max_pcr = value[0];
+        qos.txtp.pcr = value[1];
+	qos.txtp.min_pcr = value[2];
+	qos.txtp.max_cdv = value[3];
+	qos.txtp.max_sdu = value[4];
+
+	tmp = strstr(buff, "rx=");
+	if(tmp == NULL) return 0;
+	tmp += 3;
+	prev = tmp;
+	for( i = 0; i < 4; i++){
+	         tmp = strchr(prev, ',');
+		 if (tmp == NULL) return 0;
+		 memset(temp, '\0', 256);
+		 memcpy(temp, prev, tmp-prev);
+		 value[i] = (int)simple_strtoul(temp, NULL, 0);
+		 tmp ++; 
+		 prev = tmp;
+	}
+	tmp = strchr(prev, '\0');
+	if (tmp == NULL) return 0;
+	memset(temp, '\0', 256);
+        memcpy(temp, prev, tmp-prev);
+        value[i] = (int)simple_strtoul(temp, NULL, 0);
+	qos.rxtp.traffic_class = ATM_CBR;
+	qos.rxtp.max_pcr = value[0];
+        qos.rxtp.pcr = value[1];
+	qos.rxtp.min_pcr = value[2];
+	qos.rxtp.max_cdv = value[3];
+	qos.rxtp.max_sdu = value[4];
+	dprintk("mpoa: mpoa_proc.c: parse_qos(): setting qos paramameters to tx=%d,%d,%d,%d,%d rx=%d,%d,%d,%d,%d\n",
+		qos.txtp.max_pcr,
+		qos.txtp.pcr,
+		qos.txtp.min_pcr,
+		qos.txtp.max_cdv,
+		qos.txtp.max_sdu,
+		qos.rxtp.max_pcr,
+		qos.rxtp.pcr,
+		qos.rxtp.min_pcr,
+		qos.rxtp.max_cdv,
+		qos.rxtp.max_sdu
+		);
+
+        qos.aal = ATM_AAL5;
+	atm_mpoa_add_qos(ipaddr, &qos);
+	return 1;
 }
 
 /*