## Automatically generated incremental diff
## From:   linux-2.5.66-bk13
## To:     linux-2.5.67
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.5.66-bk13/Documentation/networking/generic-hdlc.txt linux-2.5.67/Documentation/networking/generic-hdlc.txt
--- linux-2.5.66-bk13/Documentation/networking/generic-hdlc.txt	2003-03-24 14:01:44.000000000 -0800
+++ linux-2.5.67/Documentation/networking/generic-hdlc.txt	2003-04-07 10:55:16.000000000 -0700
@@ -1,11 +1,13 @@
-Generic HDLC layer for Linux kernel 2.4/2.5
+Generic HDLC layer
 Krzysztof Halasa <khc@pm.waw.pl>
-May, 2001
+January, 2003
 
 
 Generic HDLC layer currently supports:
-- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP),
-- raw HDLC (IPv4 only),
+- Frame Relay (ANSI, CCITT and no LMI), with ARP support (no InARP).
+  Normal (routed) and Ethernet-bridged (Ethernet device emulation)
+  interfaces can share a single PVC.
+- raw HDLC - either IP (IPv4) interface or Ethernet device emulation.
 - Cisco HDLC,
 - PPP (uses syncppp.c),
 - X.25 (uses X.25 routines).
@@ -15,6 +17,10 @@
 - RISCom/N2 by SDL Communications Inc.
 - and others, some not in the official kernel.
 
+Ethernet device emulation (using HDLC or Frame-Relay PVC) is compatible
+with IEEE 802.1Q (VLANs) and 802.1D (Ethernet bridging).
+
+
 Make sure the hdlc.o and the hardware driver are loaded. It should
 create a number of "hdlc" (hdlc0 etc) network devices, one for each
 WAN port. You'll need the "sethdlc" utility, get it from:
@@ -58,6 +64,9 @@
   no-parity / crc16 / crc16-pr0 (CRC16 with preset zeros) / crc32-itu
   crc16-itu (CRC16 with ITU-T polynomial) / crc16-itu-pr0 - sets parity
 
+* hdlc-eth - Ethernet device emulation using HDLC. Parity and encoding
+  as above.
+
 * cisco - sets Cisco HDLC mode (IP, IPv6 and IPX supported)
   interval - time in seconds between keepalive packets
   timeout - time in seconds after last received keepalive packet before
@@ -77,7 +86,12 @@
   n392 - error threshold - both user and network
   n393 - monitored events count - both user and network
 
-* create | delete n - FR only - adds / deletes PVC interface with DLCI #n.
+Frame-Relay only:
+* create n | delete n - adds / deletes PVC interface with DLCI #n.
+  Newly created interface will be named pvc0, pvc1 etc.
+
+* create ether n | delete ether n - adds a device for Ethernet-bridged
+  frames. The device will be named pvceth0, pvceth1 etc.
 
 
 
@@ -85,30 +99,30 @@
 Board-specific issues
 ---------------------
 
-n2.o and c101.o need parameters to work (note double quotes):
+n2.o and c101.o need parameters to work:
 
-	insmod n2 hw='"io,irq,ram,ports[:io,irq,...]"'
+	insmod n2 hw=io,irq,ram,ports[:io,irq,...]
 example:
-	insmod n2 hw='"0x300,10,0xD0000,01"'
+	insmod n2 hw=0x300,10,0xD0000,01
 
 or
-	insmod c101 hw='"irq,ram[:irq,...]"
+	insmod c101 hw=irq,ram[:irq,...]
 example:
-	insmod c101 hw='"9,0xdc000"'
+	insmod c101 hw=9,0xdc000
 
 If built into the kernel, these drivers need kernel (command line) parameters:
-	n2=io,irq,ram,ports:...
+	n2.hw=io,irq,ram,ports:...
 or
-	c101=irq,ram:...
+	c101.hw=irq,ram:...
 
 
 
 If you have a problem with N2 or C101 card, you can issue the "private"
-command to see port's packet descriptor rings:
+command to see port's packet descriptor rings (in kernel logs):
 
 	sethdlc hdlc0 private
 
-The hardware driver have to be build with CONFIG_HDLC_DEBUG_RINGS.
+The hardware driver has to be build with CONFIG_HDLC_DEBUG_RINGS.
 Attaching this info to bug reports would be helpful. Anyway, let me know
 if you have problems using this.
 
diff -urN linux-2.5.66-bk13/Makefile linux-2.5.67/Makefile
--- linux-2.5.66-bk13/Makefile	2003-04-07 10:55:11.000000000 -0700
+++ linux-2.5.67/Makefile	2003-04-07 10:55:16.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 5
-SUBLEVEL = 66
-EXTRAVERSION = -bk13
+SUBLEVEL = 67
+EXTRAVERSION =
 
 # *DOCUMENTATION*
 # To see a list of typical targets execute "make help"
diff -urN linux-2.5.66-bk13/arch/i386/kernel/io_apic.c linux-2.5.67/arch/i386/kernel/io_apic.c
--- linux-2.5.66-bk13/arch/i386/kernel/io_apic.c	2003-03-24 14:00:15.000000000 -0800
+++ linux-2.5.67/arch/i386/kernel/io_apic.c	2003-04-07 10:55:16.000000000 -0700
@@ -603,6 +603,12 @@
         c = &boot_cpu_data;
 	if (irqbalance_disabled)
 		return 0;
+	
+	 /* disable irqbalance completely if there is only one processor online */
+	if (num_online_cpus() < 2) {
+		irqbalance_disabled = 1;
+		return 0;
+	}
 	/*
 	 * Enable physical balance only if more than 1 physical processor
 	 * is present
@@ -1110,13 +1116,10 @@
 		goto next;
 
 	if (current_vector > FIRST_SYSTEM_VECTOR) {
-		offset++;
+		offset = (offset + 1) & 7;
 		current_vector = FIRST_DEVICE_VECTOR + offset;
 	}
 
-	if (current_vector == FIRST_SYSTEM_VECTOR)
-		panic("ran out of interrupt sources!");
-
 	IO_APIC_VECTOR(irq) = current_vector;
 	return current_vector;
 }
diff -urN linux-2.5.66-bk13/arch/m68k/atari/stram.c linux-2.5.67/arch/m68k/atari/stram.c
--- linux-2.5.66-bk13/arch/m68k/atari/stram.c	2003-03-24 14:01:14.000000000 -0800
+++ linux-2.5.67/arch/m68k/atari/stram.c	2003-04-07 10:55:16.000000000 -0700
@@ -976,8 +976,9 @@
 
 static void do_stram_request(request_queue_t *q)
 {
-	while (!blk_queue_empty(q)) {
-		struct request *req = elv_next_request(q);
+	struct request *req;
+
+	while ((req = elv_next_request(q)) != NULL) {
 		void *start = swap_start + (req->sector << 9);
 		unsigned long len = req->current_nr_sectors << 9;
 		if ((start + len) > swap_end) {
diff -urN linux-2.5.66-bk13/drivers/acorn/block/fd1772.c linux-2.5.67/drivers/acorn/block/fd1772.c
--- linux-2.5.66-bk13/drivers/acorn/block/fd1772.c	2003-03-24 14:00:15.000000000 -0800
+++ linux-2.5.67/drivers/acorn/block/fd1772.c	2003-04-07 10:55:16.000000000 -0700
@@ -598,7 +598,7 @@
 {
 	printk("FDC1772: fd_error\n");
 	/*panic("fd1772: fd_error"); *//* DAG tmp */
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		return;
 	CURRENT->errors++;
 	if (CURRENT->errors >= MAX_ERRORS) {
@@ -1207,11 +1207,11 @@
 
 	DPRINT(("redo_fd_request: CURRENT=%p dev=%s CURRENT->sector=%ld\n",
 		CURRENT, CURRENT ? CURRENT->rq_disk->disk_name : "",
-		!blk_queue_empty(QUEUE) ? CURRENT->sector : 0));
+		CURRENT ? CURRENT->sector : 0));
 
 repeat:
 
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		goto the_end;
 
 	floppy = CURRENT->rq_disk->private_data;
diff -urN linux-2.5.66-bk13/drivers/acorn/block/mfmhd.c linux-2.5.67/drivers/acorn/block/mfmhd.c
--- linux-2.5.66-bk13/drivers/acorn/block/mfmhd.c	2003-03-24 14:00:09.000000000 -0800
+++ linux-2.5.67/drivers/acorn/block/mfmhd.c	2003-04-07 10:55:16.000000000 -0700
@@ -746,7 +746,7 @@
 		/* No - its the end of the line */
 		/* end_request's should have happened at the end of sector DMAs */
 		/* Turns Drive LEDs off - may slow it down? */
-		if (blk_queue_empty(QUEUE))
+		if (!elv_next_request(QUEUE))
 			issue_command(CMD_CKV, block, 2);
 
 		Busy = 0;
@@ -897,9 +897,9 @@
 		DBG("mfm_request: loop start\n");
 		sti();
 
-		DBG("mfm_request: before blk_queue_empty\n");
+		DBG("mfm_request: before !CURRENT\n");
 
-		if (blk_queue_empty(QUEUE)) {
+		if (!CURRENT) {
 			printk("mfm_request: Exiting due to empty queue (pre)\n");
 			do_mfm = NULL;
 			Busy = 0;
diff -urN linux-2.5.66-bk13/drivers/block/DAC960.c linux-2.5.67/drivers/block/DAC960.c
--- linux-2.5.66-bk13/drivers/block/DAC960.c	2003-03-24 14:01:15.000000000 -0800
+++ linux-2.5.67/drivers/block/DAC960.c	2003-04-07 10:55:17.000000000 -0700
@@ -3181,10 +3181,10 @@
      return false;
 
   while (true) {
-      if (blk_queue_empty(RequestQueue))
-          return false;
-
       Request = elv_next_request(RequestQueue);
+      if (!Request)
+	      return false;
+
       Command = DAC960_AllocateCommand(Controller);
       if (Command != NULL)
           break;
diff -urN linux-2.5.66-bk13/drivers/block/acsi.c linux-2.5.67/drivers/block/acsi.c
--- linux-2.5.66-bk13/drivers/block/acsi.c	2003-03-24 14:01:48.000000000 -0800
+++ linux-2.5.67/drivers/block/acsi.c	2003-04-07 10:55:17.000000000 -0700
@@ -763,7 +763,7 @@
 static void bad_rw_intr( void )
 
 {
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		return;
 
 	if (++CURRENT->errors >= MAX_ERRORS)
@@ -837,7 +837,7 @@
 
 	do_acsi = NULL;
 	printk( KERN_ERR "ACSI timeout\n" );
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 	    return;
 	if (++CURRENT->errors >= MAX_ERRORS) {
 #ifdef DEBUG
@@ -956,7 +956,7 @@
 	if (do_acsi)
 		return;
 
-	if (blk_queue_empty(QUEUE)) {
+	if (!CURRENT) {
 		do_acsi = NULL;
 		ENABLE_IRQ();
 		stdma_release();
diff -urN linux-2.5.66-bk13/drivers/block/amiflop.c linux-2.5.67/drivers/block/amiflop.c
--- linux-2.5.66-bk13/drivers/block/amiflop.c	2003-03-24 14:00:24.000000000 -0800
+++ linux-2.5.67/drivers/block/amiflop.c	2003-04-07 10:55:17.000000000 -0700
@@ -1365,7 +1365,7 @@
 	unsigned long flags;
 
  repeat:
-	if (blk_queue_empty(QUEUE)) {
+	if (!CURRENT) {
 		/* Nothing left to do */
 		return;
 	}
diff -urN linux-2.5.66-bk13/drivers/block/ataflop.c linux-2.5.67/drivers/block/ataflop.c
--- linux-2.5.66-bk13/drivers/block/ataflop.c	2003-03-24 14:00:07.000000000 -0800
+++ linux-2.5.67/drivers/block/ataflop.c	2003-04-07 10:55:17.000000000 -0700
@@ -627,7 +627,7 @@
 		return;
 	}
 
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		return;
 
 	CURRENT->errors++;
@@ -1435,14 +1435,14 @@
 	struct atari_floppy_struct *floppy;
 
 	DPRINT(("redo_fd_request: CURRENT=%p dev=%s CURRENT->sector=%ld\n",
-		CURRENT, !blk_queue_empty(QUEUE) ? CURRENT->rq_disk->disk_name : "",
-		!blk_queue_empty(QUEUE) ? CURRENT->sector : 0 ));
+		CURRENT, CURRENT ? CURRENT->rq_disk->disk_name : "",
+		CURRENT ? CURRENT->sector : 0 ));
 
 	IsFormatting = 0;
 
 repeat:
 
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		goto the_end;
 
 	floppy = CURRENT->rq_disk->private_data;
diff -urN linux-2.5.66-bk13/drivers/block/cciss.c linux-2.5.67/drivers/block/cciss.c
--- linux-2.5.66-bk13/drivers/block/cciss.c	2003-03-24 14:01:46.000000000 -0800
+++ linux-2.5.67/drivers/block/cciss.c	2003-04-07 10:55:17.000000000 -0700
@@ -1876,10 +1876,10 @@
 		goto startio;
 
 queue:
-	if (blk_queue_empty(q))
+	creq = elv_next_request(q);
+	if (!creq)
 		goto startio;
 
-	creq = elv_next_request(q);
 	if (creq->nr_phys_segments > MAXSGENTRIES)
                 BUG();
 
diff -urN linux-2.5.66-bk13/drivers/block/cpqarray.c linux-2.5.67/drivers/block/cpqarray.c
--- linux-2.5.66-bk13/drivers/block/cpqarray.c	2003-03-24 14:00:03.000000000 -0800
+++ linux-2.5.67/drivers/block/cpqarray.c	2003-04-07 10:55:17.000000000 -0700
@@ -786,10 +786,10 @@
 		goto startio;
 
 queue_next:
-	if (blk_queue_empty(q))
+	creq = elv_next_request(q);
+	if (!creq)
 		goto startio;
 
-	creq = elv_next_request(q);
 	if (creq->nr_phys_segments > SG_MAX)
 		BUG();
 
diff -urN linux-2.5.66-bk13/drivers/block/ll_rw_blk.c linux-2.5.67/drivers/block/ll_rw_blk.c
--- linux-2.5.66-bk13/drivers/block/ll_rw_blk.c	2003-04-07 10:55:12.000000000 -0700
+++ linux-2.5.67/drivers/block/ll_rw_blk.c	2003-04-07 10:55:17.000000000 -0700
@@ -1698,7 +1698,7 @@
 again:
 	insert_here = NULL;
 
-	if (blk_queue_empty(q)) {
+	if (elv_queue_empty(q)) {
 		blk_plug_device(q);
 		goto get_rq;
 	}
diff -urN linux-2.5.66-bk13/drivers/block/nbd.c linux-2.5.67/drivers/block/nbd.c
--- linux-2.5.66-bk13/drivers/block/nbd.c	2003-03-24 14:00:10.000000000 -0800
+++ linux-2.5.67/drivers/block/nbd.c	2003-04-07 10:55:17.000000000 -0700
@@ -337,9 +337,9 @@
 
 static void do_nbd_request(request_queue_t * q)
 {
-
-	while (!blk_queue_empty(q)) {
-		struct request *req = elv_next_request(q);
+	struct request *req;
+	
+	while ((req = elv_next_request(q)) != NULL) {
 		struct nbd_device *lo;
 
 		if (!(req->flags & REQ_CMD))
diff -urN linux-2.5.66-bk13/drivers/block/paride/pcd.c linux-2.5.67/drivers/block/paride/pcd.c
--- linux-2.5.66-bk13/drivers/block/paride/pcd.c	2003-03-24 14:01:46.000000000 -0800
+++ linux-2.5.67/drivers/block/paride/pcd.c	2003-04-07 10:55:17.000000000 -0700
@@ -739,9 +739,10 @@
 	if (pcd_busy)
 		return;
 	while (1) {
-		if (blk_queue_empty(q))
-			return;
 		pcd_req = elv_next_request(q);
+		if (!pcd_req)
+			return;
+
 		if (rq_data_dir(pcd_req) == READ) {
 			struct pcd_unit *cd = pcd_req->rq_disk->private_data;
 			if (cd != pcd_current)
diff -urN linux-2.5.66-bk13/drivers/block/paride/pd.c linux-2.5.67/drivers/block/paride/pd.c
--- linux-2.5.66-bk13/drivers/block/paride/pd.c	2003-03-24 14:01:16.000000000 -0800
+++ linux-2.5.67/drivers/block/paride/pd.c	2003-04-07 10:55:17.000000000 -0700
@@ -726,10 +726,10 @@
 	if (pd_busy)
 		return;
 repeat:
-	if (blk_queue_empty(q))
+	pd_req = elv_next_request(q);
+	if (!pd_req)
 		return;
 
-	pd_req = elv_next_request(q);
 	pd_block = pd_req->sector;
 	pd_run = pd_req->nr_sectors;
 	pd_count = pd_req->current_nr_sectors;
diff -urN linux-2.5.66-bk13/drivers/block/ps2esdi.c linux-2.5.67/drivers/block/ps2esdi.c
--- linux-2.5.66-bk13/drivers/block/ps2esdi.c	2003-03-24 13:59:52.000000000 -0800
+++ linux-2.5.67/drivers/block/ps2esdi.c	2003-04-07 10:55:17.000000000 -0700
@@ -477,12 +477,9 @@
 	/* since, this routine is called with interrupts cleared - they 
 	   must be before it finishes  */
 
-	/* standard procedure to ensure that requests are really on the
-	   list + sanity checks.                     */
-	if (blk_queue_empty(q))
-		return;
-
 	req = elv_next_request(q);
+	if (!req)
+		return;
 
 #if 0
 	printk("%s:got request. device : %s command : %d  sector : %ld count : %ld, buffer: %p\n",
diff -urN linux-2.5.66-bk13/drivers/block/swim3.c linux-2.5.67/drivers/block/swim3.c
--- linux-2.5.66-bk13/drivers/block/swim3.c	2003-03-24 14:00:18.000000000 -0800
+++ linux-2.5.67/drivers/block/swim3.c	2003-04-07 10:55:17.000000000 -0700
@@ -305,6 +305,7 @@
 
 static void start_request(struct floppy_state *fs)
 {
+	struct request *req;
 	unsigned long x;
 
 	if (fs->state == idle && fs->wanted) {
@@ -312,8 +313,7 @@
 		wake_up(&fs->wait);
 		return;
 	}
-	while (!blk_queue_empty(&swim3_queue) && fs->state == idle) {
-		struct request *req = elv_next_request(&swim3_queue);
+	while (fs->state == idle && (req = elv_next_request(&swim3_queue))) {
 #if 0
 		printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
 		       req->rq_disk->disk_name, req->cmd,
diff -urN linux-2.5.66-bk13/drivers/block/swim_iop.c linux-2.5.67/drivers/block/swim_iop.c
--- linux-2.5.66-bk13/drivers/block/swim_iop.c	2003-03-24 13:59:53.000000000 -0800
+++ linux-2.5.67/drivers/block/swim_iop.c	2003-04-07 10:55:17.000000000 -0700
@@ -516,7 +516,7 @@
 		wake_up(&fs->wait);
 		return;
 	}
-	while (!blk_queue_empty(&swim_queue) && fs->state == idle) {
+	while (CURRENT && fs->state == idle) {
 		if (CURRENT->bh && !buffer_locked(CURRENT->bh))
 			panic("floppy: block not locked");
 #if 0
diff -urN linux-2.5.66-bk13/drivers/block/xd.c linux-2.5.67/drivers/block/xd.c
--- linux-2.5.66-bk13/drivers/block/xd.c	2003-03-24 14:01:17.000000000 -0800
+++ linux-2.5.67/drivers/block/xd.c	2003-04-07 10:55:17.000000000 -0700
@@ -282,11 +282,12 @@
 /* do_xd_request: handle an incoming request */
 static void do_xd_request (request_queue_t * q)
 {
+	struct request *req;
+
 	if (xdc_busy)
 		return;
 
-	while (!blk_queue_empty(q)) {
-		struct request *req = elv_next_request(q);
+	while ((req = elv_next_request(q)) != NULL) {
 		unsigned block = req->sector;
 		unsigned count = req->nr_sectors;
 		int rw = rq_data_dir(req);
diff -urN linux-2.5.66-bk13/drivers/block/z2ram.c linux-2.5.67/drivers/block/z2ram.c
--- linux-2.5.66-bk13/drivers/block/z2ram.c	2003-03-24 14:00:54.000000000 -0800
+++ linux-2.5.67/drivers/block/z2ram.c	2003-04-07 10:55:17.000000000 -0700
@@ -73,8 +73,8 @@
 
 static void do_z2_request(request_queue_t *q)
 {
-	while (!blk_queue_empty(q)) {
-		struct request *req = elv_next_request(q);
+	struct request *req;
+	while ((req = elv_next_request) != NULL) {
 		unsigned long start = req->sector << 9;
 		unsigned long len  = req->current_nr_sectors << 9;
 
diff -urN linux-2.5.66-bk13/drivers/cdrom/aztcd.c linux-2.5.67/drivers/cdrom/aztcd.c
--- linux-2.5.66-bk13/drivers/cdrom/aztcd.c	2003-03-24 14:01:26.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/aztcd.c	2003-04-07 10:55:17.000000000 -0700
@@ -231,7 +231,7 @@
 
 static int current_valid(void)
 {
-        return !blk_queue_empty(QUEUE) &&
+        return CURRENT &&
 		CURRENT->cmd == READ &&
 		CURRENT->sector != -1;
 }
diff -urN linux-2.5.66-bk13/drivers/cdrom/cdu31a.c linux-2.5.67/drivers/cdrom/cdu31a.c
--- linux-2.5.66-bk13/drivers/cdrom/cdu31a.c	2003-03-24 14:01:11.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/cdu31a.c	2003-04-07 10:55:17.000000000 -0700
@@ -1583,13 +1583,13 @@
 		 * The beginning here is stolen from the hard disk driver.  I hope
 		 * it's right.
 		 */
-		if (blk_queue_empty(q))
+		req = elv_next_request(q);
+		if (!req)
 			goto end_do_cdu31a_request;
 
 		if (!sony_spun_up)
 			scd_spinup();
 
-		req = elv_next_request(q);
 		block = req->sector;
 		nblock = req->nr_sectors;
 
diff -urN linux-2.5.66-bk13/drivers/cdrom/cm206.c linux-2.5.67/drivers/cdrom/cm206.c
--- linux-2.5.66-bk13/drivers/cdrom/cm206.c	2003-03-24 14:01:25.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/cm206.c	2003-04-07 10:55:17.000000000 -0700
@@ -855,10 +855,10 @@
 	struct request *req;
 
 	while (1) {	/* repeat until all requests have been satisfied */
-		if (blk_queue_empty(q))
+		req = elv_next_request(q);
+		if (!req)
 			return;
 
-		req = elv_next_request(q);
 		if (req->cmd != READ) {
 			debug(("Non-read command %d on cdrom\n", req->cmd));
 			end_request(req, 0);
diff -urN linux-2.5.66-bk13/drivers/cdrom/gscd.c linux-2.5.67/drivers/cdrom/gscd.c
--- linux-2.5.66-bk13/drivers/cdrom/gscd.c	2003-03-24 13:59:54.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/gscd.c	2003-04-07 10:55:17.000000000 -0700
@@ -257,10 +257,10 @@
 	unsigned int nsect;
 
 repeat:
-	if (blk_queue_empty(&gscd_queue))
+	req = elv_next_request(&gscd_queue);
+	if (!req)
 		return;
 
-	req = elv_next_request(&gscd_queue);
 	block = req->sector;
 	nsect = req->nr_sectors;
 
diff -urN linux-2.5.66-bk13/drivers/cdrom/mcd.c linux-2.5.67/drivers/cdrom/mcd.c
--- linux-2.5.66-bk13/drivers/cdrom/mcd.c	2003-03-24 14:00:50.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/mcd.c	2003-04-07 10:55:17.000000000 -0700
@@ -125,7 +125,7 @@
 
 static int current_valid(void)
 {
-        return !blk_queue_empty(QUEUE) &&
+        return CURRENT &&
 		CURRENT->cmd == READ &&
 		CURRENT->sector != -1;
 }
diff -urN linux-2.5.66-bk13/drivers/cdrom/mcdx.c linux-2.5.67/drivers/cdrom/mcdx.c
--- linux-2.5.66-bk13/drivers/cdrom/mcdx.c	2003-03-24 14:00:24.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/mcdx.c	2003-04-07 10:55:17.000000000 -0700
@@ -582,10 +582,10 @@
 
       again:
 
-	if (blk_queue_empty(q))
+	req = elv_next_request(q);
+	if (!req)
 		return;
 
-	req = elv_next_request(q);
 	stuffp = req->rq_disk->private_data;
 
 	if (!stuffp->present) {
diff -urN linux-2.5.66-bk13/drivers/cdrom/optcd.c linux-2.5.67/drivers/cdrom/optcd.c
--- linux-2.5.66-bk13/drivers/cdrom/optcd.c	2003-04-07 10:55:12.000000000 -0700
+++ linux-2.5.67/drivers/cdrom/optcd.c	2003-04-07 10:55:17.000000000 -0700
@@ -977,7 +977,7 @@
 
 static int current_valid(void)
 {
-        return !blk_queue_empty(QUEUE) &&
+        return CURRENT &&
 		CURRENT->cmd == READ &&
 		CURRENT->sector != -1;
 }
diff -urN linux-2.5.66-bk13/drivers/cdrom/sbpcd.c linux-2.5.67/drivers/cdrom/sbpcd.c
--- linux-2.5.66-bk13/drivers/cdrom/sbpcd.c	2003-03-24 14:00:08.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/sbpcd.c	2003-04-07 10:55:17.000000000 -0700
@@ -4852,7 +4852,9 @@
 #ifdef DEBUG_GTL
 	xnr=++xx_nr;
 
-	if(blk_queue_empty(q))
+	req = elv_next_request(q);
+
+	if (!req)
 	{
 		printk( "do_sbpcd_request[%di](NULL), Pid:%d, Time:%li\n",
 			xnr, current->pid, jiffies);
@@ -4861,15 +4863,14 @@
 		return;
 	}
 
-	req = elv_next_request(q);
-
 	printk(" do_sbpcd_request[%di](%p:%ld+%ld), Pid:%d, Time:%li\n",
 		xnr, req, req->sector, req->nr_sectors, current->pid, jiffies);
 #endif
-	if (blk_queue_empty(q))
+
+	req = elv_next_request(q);	/* take out our request so no other */
+	if (!req)
 		return;
 
-	req = elv_next_request(q);		/* take out our request so no other */
 	if (req -> sector == -1)
 		end_request(req, 0);
 	spin_unlock_irq(q->queue_lock);
diff -urN linux-2.5.66-bk13/drivers/cdrom/sjcd.c linux-2.5.67/drivers/cdrom/sjcd.c
--- linux-2.5.66-bk13/drivers/cdrom/sjcd.c	2003-03-24 14:00:55.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/sjcd.c	2003-04-07 10:55:17.000000000 -0700
@@ -1064,7 +1064,7 @@
 
 static int current_valid(void)
 {
-        return !blk_queue_empty(QUEUE) &&
+        return CURRENT &&
 		CURRENT->cmd == READ &&
 		CURRENT->sector != -1;
 }
diff -urN linux-2.5.66-bk13/drivers/cdrom/sonycd535.c linux-2.5.67/drivers/cdrom/sonycd535.c
--- linux-2.5.66-bk13/drivers/cdrom/sonycd535.c	2003-03-24 14:00:15.000000000 -0800
+++ linux-2.5.67/drivers/cdrom/sonycd535.c	2003-04-07 10:55:17.000000000 -0700
@@ -797,10 +797,10 @@
 	Byte cmd[2];
 
 	while (1) {
-		if (blk_queue_empty(q))
+		req = elv_next_request(q);
+		if (!req)
 			return;
 
-		req = elv_next_request(q);
 		block = req->sector;
 		nsect = req->nr_sectors;
 		if (!(req->flags & REQ_CMD))
diff -urN linux-2.5.66-bk13/drivers/char/hw_random.c linux-2.5.67/drivers/char/hw_random.c
--- linux-2.5.66-bk13/drivers/char/hw_random.c	2003-04-07 10:55:12.000000000 -0700
+++ linux-2.5.67/drivers/char/hw_random.c	2003-04-07 10:55:17.000000000 -0700
@@ -126,7 +126,7 @@
 	rng_hw_via,
 };
 
-static struct rng_operations rng_vendor_ops[] __initdata = {
+static struct rng_operations rng_vendor_ops[] = {
 	/* rng_hw_none */
 	{ },
 
diff -urN linux-2.5.66-bk13/drivers/ide/ide-io.c linux-2.5.67/drivers/ide/ide-io.c
--- linux-2.5.66-bk13/drivers/ide/ide-io.c	2003-03-24 14:01:17.000000000 -0800
+++ linux-2.5.67/drivers/ide/ide-io.c	2003-04-07 10:55:17.000000000 -0700
@@ -673,7 +673,8 @@
 	best = NULL;
 	drive = hwgroup->drive;
 	do {
-		if (!blk_queue_empty(&drive->queue) && (!drive->sleep || time_after_eq(jiffies, drive->sleep))) {
+		if ((!drive->sleep || time_after_eq(jiffies, drive->sleep))
+		    && !elv_queue_empty(&drive->queue)) {
 			if (!best
 			 || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep)))
 			 || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive))))
diff -urN linux-2.5.66-bk13/drivers/ide/legacy/hd.c linux-2.5.67/drivers/ide/legacy/hd.c
--- linux-2.5.66-bk13/drivers/ide/legacy/hd.c	2003-03-24 13:59:53.000000000 -0800
+++ linux-2.5.67/drivers/ide/legacy/hd.c	2003-04-07 10:55:17.000000000 -0700
@@ -194,7 +194,7 @@
 static void dump_status (const char *msg, unsigned int stat)
 {
 	char *name = "hd?";
-	if (!blk_queue_empty(QUEUE))
+	if (CURRENT)
 		name = CURRENT->rq_disk->disk_name;
 
 #ifdef VERBOSE_ERRORS
@@ -223,7 +223,7 @@
 		if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
 			printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
 				inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
-			if (!blk_queue_empty(QUEUE))
+			if (CURRENT)
 				printk(", sector=%ld", CURRENT->sector);
 		}
 		printk("\n");
@@ -403,8 +403,8 @@
  */
 static void bad_rw_intr(void)
 {
-	if (!blk_queue_empty(QUEUE)) {
-		struct request *req = CURRENT;
+	struct request *req = CURRENT;
+	if (req != NULL) {
 		struct hd_i_struct *disk = req->rq_disk->private_data;
 		if (++req->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
 			end_request(req, 0);
@@ -469,7 +469,7 @@
 #if (HD_DELAY > 0)
 	last_req = read_timer();
 #endif
-	if (!blk_queue_empty(QUEUE))
+	if (elv_next_request(QUEUE))
 		hd_request();
 	return;
 }
@@ -532,7 +532,7 @@
 
 	do_hd = NULL;
 
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		return;
 
 	disable_irq(HD_IRQ);
@@ -588,11 +588,11 @@
 	del_timer(&device_timer);
 	local_irq_enable();
 
-	if (blk_queue_empty(QUEUE)) {
+	req = CURRENT;
+	if (!req) {
 		do_hd = NULL;
 		return;
 	}
-	req = CURRENT;
 
 	if (reset) {
 		local_irq_disable();
diff -urN linux-2.5.66-bk13/drivers/ide/legacy/hd98.c linux-2.5.67/drivers/ide/legacy/hd98.c
--- linux-2.5.66-bk13/drivers/ide/legacy/hd98.c	2003-03-24 14:01:12.000000000 -0800
+++ linux-2.5.67/drivers/ide/legacy/hd98.c	2003-04-07 10:55:17.000000000 -0700
@@ -197,7 +197,7 @@
 {
 	char devc;
 
-	devc = !blk_queue_empty(QUEUE) ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
+	devc = CURRENT ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?';
 #ifdef VERBOSE_ERRORS
 	printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff);
 	if (stat & BUSY_STAT)	printk("Busy ");
@@ -224,7 +224,7 @@
 		if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
 			printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL),
 				inb(HD_CURRENT) & 0xf, inb(HD_SECTOR));
-			if (!blk_queue_empty(QUEUE))
+			if (CURRENT)
 				printk(", sector=%ld", CURRENT->sector);
 		}
 		printk("\n");
@@ -400,7 +400,7 @@
 {
 	int dev;
 
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		return;
 	dev = DEVICE_NR(CURRENT->rq_dev);
 	if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) {
@@ -463,7 +463,7 @@
 #if (HD_DELAY > 0)
 	last_req = read_timer();
 #endif
-	if (!blk_queue_empty(QUEUE))
+	if (CURRENT)
 		hd_request();
 	return;
 }
@@ -525,7 +525,7 @@
 
 	do_hd = NULL;
 
-	if (blk_queue_empty(QUEUE))
+	if (!CURRENT)
 		return;
 
 	disable_irq(HD_IRQ);
@@ -579,7 +579,7 @@
 	del_timer(&device_timer);
 	local_irq_enable();
 
-	if (blk_queue_empty(QUEUE)) {
+	if (!CURRENT) {
 		do_hd = NULL;
 		return;
 	}
diff -urN linux-2.5.66-bk13/drivers/message/i2o/i2o_block.c linux-2.5.67/drivers/message/i2o/i2o_block.c
--- linux-2.5.66-bk13/drivers/message/i2o/i2o_block.c	2003-03-24 14:01:12.000000000 -0800
+++ linux-2.5.67/drivers/message/i2o/i2o_block.c	2003-04-07 10:55:17.000000000 -0700
@@ -783,14 +783,12 @@
 	struct i2ob_device *dev;
 	u32 m;
 	
-	while (blk_queue_empty(q)) {
+	while ((req = elv_next_request(q)) != NULL) {
 		/*
 		 *	On an IRQ completion if there is an inactive
 		 *	request on the queue head it means it isnt yet
 		 *	ready to dispatch.
 		 */
-		req = elv_next_request(q);
-
 		if(req->rq_status == RQ_INACTIVE)
 			return;
 
diff -urN linux-2.5.66-bk13/drivers/mtd/ftl.c linux-2.5.67/drivers/mtd/ftl.c
--- linux-2.5.66-bk13/drivers/mtd/ftl.c	2003-03-24 14:01:18.000000000 -0800
+++ linux-2.5.67/drivers/mtd/ftl.c	2003-04-07 10:55:17.000000000 -0700
@@ -1135,9 +1135,9 @@
 
 	do {
 		//	    sti();
-		if (blk_queue_empty(q))
-			return;
 		req = elv_next_request(q);
+		if (!req)
+			return;
 		part = req->rq_disk->private_data;
 		if (part) {
 			ret = 0;
diff -urN linux-2.5.66-bk13/drivers/mtd/mtdblock.c linux-2.5.67/drivers/mtd/mtdblock.c
--- linux-2.5.66-bk13/drivers/mtd/mtdblock.c	2003-03-24 14:00:18.000000000 -0800
+++ linux-2.5.67/drivers/mtd/mtdblock.c	2003-04-07 10:55:17.000000000 -0700
@@ -384,11 +384,11 @@
 static struct request_queue mtd_queue;
 static void handle_mtdblock_request(void)
 {
+	struct request *req;
 	struct mtdblk_dev *mtdblk;
 	unsigned int res;
 
-	while (!blk_queue_empty(&mtd_queue)) {
-		struct request *req = elv_next_request(&mtd_queue);
+	while ((req = elv_next_request(&mtd_queue) != NULL) {
 		struct mtdblk_dev **p = req->rq_disk->private_data;
 		spin_unlock_irq(mtd_queue.queue_lock);
 		mtdblk = *p;
@@ -458,7 +458,7 @@
 		add_wait_queue(&thr_wq, &wait);
 		set_current_state(TASK_INTERRUPTIBLE);
 		spin_lock_irq(mtd_queue.queue_lock);
-		if (blk_queue_empty(&mtd_queue) || blk_queue_plugged(&mtd_queue)) {
+		if (!elv_next_request(&mtd_queue) || blk_queue_plugged(&mtd_queue)) {
 			spin_unlock_irq(mtd_queue.queue_lock);
 			schedule();
 			remove_wait_queue(&thr_wq, &wait); 
diff -urN linux-2.5.66-bk13/drivers/mtd/mtdblock_ro.c linux-2.5.67/drivers/mtd/mtdblock_ro.c
--- linux-2.5.66-bk13/drivers/mtd/mtdblock_ro.c	2003-03-24 14:00:35.000000000 -0800
+++ linux-2.5.67/drivers/mtd/mtdblock_ro.c	2003-04-07 10:55:17.000000000 -0700
@@ -93,8 +93,9 @@
 
 static void mtdblock_request(request_queue_t *q)
 {
-	while (!blk_queue_empty(q)) {
-		struct request *req = elv_next_request(q);
+	struct request *req;
+	
+	while ((req = elv_next_request(q)) != NULL) {
 		struct mtdro_dev *mdev = req->rq_disk->private_data;
 		struct mtd_info *mtd = mdev->mtd;
 		unsigned int res;
diff -urN linux-2.5.66-bk13/drivers/mtd/nftlcore.c linux-2.5.67/drivers/mtd/nftlcore.c
--- linux-2.5.66-bk13/drivers/mtd/nftlcore.c	2003-03-24 13:59:54.000000000 -0800
+++ linux-2.5.67/drivers/mtd/nftlcore.c	2003-04-07 10:55:17.000000000 -0700
@@ -780,8 +780,9 @@
 
 void nftl_request(struct request_queue *q)
 {
-	while (!blk_queue_empty(q)) {
-		struct request *req = elv_next_request(q);
+	struct request *req;
+	
+	while ((req = elv_next_request(q)) != NULL) {
 		unsigned block = req->sector;
 		unsigned nsect = req->current_nr_sectors;
 		char *buffer = req->buffer;
diff -urN linux-2.5.66-bk13/drivers/net/wan/Kconfig linux-2.5.67/drivers/net/wan/Kconfig
--- linux-2.5.66-bk13/drivers/net/wan/Kconfig	2003-03-24 14:00:10.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/Kconfig	2003-04-07 10:55:17.000000000 -0700
@@ -253,6 +253,11 @@
 	  Generic HDLC driver currently supports raw HDLC, Cisco HDLC, Frame
 	  Relay, synchronous Point-to-Point Protocol (PPP) and X.25.
 
+ 	  If you want to compile the driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read Documentation/modules.txt.  The module
+	  will be called hdlc.o.
+
 	  If unsure, say N here.
 
 config HDLC_RAW
@@ -264,6 +269,17 @@
 
 	  If unsure, say N here.
 
+config HDLC_RAW_ETH
+	bool "Raw HDLC Ethernet device support"
+	depends on HDLC
+	help
+	  Say Y to this option if you want generic HDLC driver to support
+	  raw HDLC Ethernet device emulation over WAN (Wide Area Network)
+	  connections.
+	  You will need it for Ethernet over HDLC bridges.
+
+	  If unsure, say N here.
+
 config HDLC_CISCO
 	bool "Cisco HDLC support"
 	depends on HDLC
@@ -344,6 +360,11 @@
 
 	  Note that N2csu and N2dds cards are not supported by this driver.
 
+	  If you want to compile the driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt>.  The module
+	  will be called n2.o.
+
 	  If unsure, say N here.
 
 config C101
@@ -354,6 +375,11 @@
 	  Technologies Co., Ltd. If you have such a card,
 	  say Y here and see <http://hq.pm.waw.pl/pub/hdlc/>
 
+	  If you want to compile the driver as a module ( = code which can be
+	  inserted in and removed from the running kernel whenever you want),
+	  say M here and read <file:Documentation/modules.txt>.  The module
+	  will be called c101.o.
+
 	  If unsure, say N here.
 
 config FARSYNC
@@ -377,18 +403,33 @@
 config HDLC_DEBUG_PKT
 	bool "Debug received/transmitted packets"
 	depends on HDLC
+	help
+	  This option is for developers only - do NOT use on production
+	  systems.
 
 config HDLC_DEBUG_HARD_HEADER
 	bool "Debug hard_header routines"
 	depends on HDLC
+	help
+	  This option is for developers only - do NOT use on production
+	  systems.
 
 config HDLC_DEBUG_ECN
 	bool "Debug FECN/BECN conditions"
 	depends on HDLC
+	help
+	  This option is for developers only - do NOT use on production
+	  systems.
 
 config HDLC_DEBUG_RINGS
 	bool "Debug RX/TX packet rings"
 	depends on HDLC
+	help
+	  If you answer Y here you will be able to get a diagnostic dump of
+	  port's TX and RX packet rings, using "sethdlc hdlcX private"
+	  command. It does not affect normal operations.
+
+	  If unsure, say Y here.
 
 config DLCI
 	tristate "Frame relay DLCI support"
diff -urN linux-2.5.66-bk13/drivers/net/wan/Makefile linux-2.5.67/drivers/net/wan/Makefile
--- linux-2.5.66-bk13/drivers/net/wan/Makefile	2003-03-24 14:00:48.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/Makefile	2003-04-07 10:55:17.000000000 -0700
@@ -19,6 +19,7 @@
 
 hdlc-y				:= hdlc_generic.o
 hdlc-$(CONFIG_HDLC_RAW)		+= hdlc_raw.o
+hdlc-$(CONFIG_HDLC_RAW_ETH)	+= hdlc_raw_eth.o
 hdlc-$(CONFIG_HDLC_CISCO)	+= hdlc_cisco.o
 hdlc-$(CONFIG_HDLC_FR)		+= hdlc_fr.o
 hdlc-$(CONFIG_HDLC_PPP)		+= hdlc_ppp.o
diff -urN linux-2.5.66-bk13/drivers/net/wan/c101.c linux-2.5.67/drivers/net/wan/c101.c
--- linux-2.5.66-bk13/drivers/net/wan/c101.c	2003-03-24 14:01:14.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/c101.c	2003-04-07 10:55:17.000000000 -0700
@@ -1,12 +1,11 @@
 /*
  * Moxa C101 synchronous serial card driver for Linux
  *
- * Copyright (C) 2000-2002 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000-2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  *
  * For information see http://hq.pm.waw.pl/hdlc/
  *
@@ -23,6 +22,7 @@
 #include <linux/string.h>
 #include <linux/errno.h>
 #include <linux/init.h>
+#include <linux/moduleparam.h>
 #include <linux/netdevice.h>
 #include <linux/hdlc.h>
 #include <linux/delay.h>
@@ -31,7 +31,7 @@
 #include "hd64570.h"
 
 
-static const char* version = "Moxa C101 driver version: 1.10";
+static const char* version = "Moxa C101 driver version: 1.14";
 static const char* devname = "C101";
 
 #define C101_PAGE 0x1D00
@@ -41,6 +41,10 @@
 #define C101_MAPPED_RAM_SIZE 0x4000
 
 #define RAM_SIZE (256 * 1024)
+#define TX_RING_BUFFERS 10
+#define RX_RING_BUFFERS ((RAM_SIZE - C101_WINDOW_SIZE) /		\
+			 (sizeof(pkt_desc) + HDLC_MAX_MRU) - TX_RING_BUFFERS)
+
 #define CLOCK_BASE 9830400	/* 9.8304 MHz */
 #define PAGE0_ALWAYS_MAPPED
 
@@ -52,20 +56,20 @@
 	spinlock_t lock;	/* TX lock */
 	u8 *win0base;		/* ISA window base address */
 	u32 phy_winbase;	/* ISA physical base address */
-	u16 buff_offset;	/* offset of first buffer of first channel */
 	sync_serial_settings settings;
+	int rxpart;		/* partial frame received, next frame invalid*/
 	unsigned short encoding;
 	unsigned short parity;
+	u16 rx_ring_buffers;	/* number of buffers in a ring */
+	u16 tx_ring_buffers;
+	u16 buff_offset;	/* offset of first buffer of first channel */
+	u16 rxin;		/* rx ring buffer 'in' pointer */
+	u16 txin;		/* tx ring buffer 'in' and 'last' pointers */
+	u16 txlast;
 	u8 rxs, txs, tmc;	/* SCA registers */
 	u8 irq;			/* IRQ (3-15) */
-	u8 ring_buffers;	/* number of buffers in a ring */
 	u8 page;
 
-	u8 rxin;		/* rx ring buffer 'in' pointer */
-	u8 txin;		/* tx ring buffer 'in' and 'last' pointers */
-	u8 txlast;
-	u8 rxpart;		/* partial frame received, next frame invalid*/
-
 	struct card_s *next_card;
 }card_t;
 
@@ -78,7 +82,12 @@
 #define sca_in(reg, card)	   readb((card)->win0base + C101_SCA + (reg))
 #define sca_out(value, reg, card)  writeb(value, (card)->win0base + C101_SCA + (reg))
 #define sca_inw(reg, card)	   readw((card)->win0base + C101_SCA + (reg))
-#define sca_outw(value, reg, card) writew(value, (card)->win0base + C101_SCA + (reg))
+
+/* EDA address register must be set in EDAL, EDAH order - 8 bit ISA bus */
+#define sca_outw(value, reg, card) do { \
+	writeb(value & 0xFF, (card)->win0base + C101_SCA + (reg)); \
+	writeb((value >> 8 ) & 0xFF, (card)->win0base + C101_SCA + (reg+1));\
+} while(0)
 
 #define port_to_card(port)	   (port)
 #define log_node(port)		   (0)
@@ -146,11 +155,16 @@
 {
 	hdlc_device *hdlc = dev_to_hdlc(dev);
 	port_t *port = hdlc_to_port(hdlc);
+
+	if (!try_module_get(THIS_MODULE))
+		return -EFAULT;	/* rmmod in progress */
+
 	int result = hdlc_open(hdlc);
-	if (result)
+	if (result) {
 		return result;
+		module_put(THIS_MODULE);
+	}
 
-	MOD_INC_USE_COUNT;
 	writeb(1, port->win0base + C101_DTR);
 	sca_out(0, MSCI1_OFFSET + CTL, port); /* RTS uses ch#2 output */
 	sca_open(hdlc);
@@ -168,7 +182,7 @@
 	writeb(0, port->win0base + C101_DTR);
 	sca_out(CTL_NORTS, MSCI1_OFFSET + CTL, port);
 	hdlc_close(hdlc);
-	MOD_DEC_USE_COUNT;
+	module_put(THIS_MODULE);
 	return 0;
 }
 
@@ -229,6 +243,8 @@
 
 static void c101_destroy_card(card_t *card)
 {
+	readb(card->win0base + C101_PAGE); /* Resets SCA? */
+
 	if (card->irq)
 		free_irq(card->irq, card);
 
@@ -242,7 +258,7 @@
 
 
 
-static int c101_run(unsigned long irq, unsigned long winbase)
+static int __init c101_run(unsigned long irq, unsigned long winbase)
 {
 	struct net_device *dev;
 	card_t *card;
@@ -285,9 +301,10 @@
 		return -EBUSY;
 	}
 
-	/* 2 rings required for 1 port */
-	card->ring_buffers = (RAM_SIZE -C101_WINDOW_SIZE) / (2 * HDLC_MAX_MRU);
-	printk(KERN_DEBUG "c101: using %u packets rings\n",card->ring_buffers);
+	card->tx_ring_buffers = TX_RING_BUFFERS;
+	card->rx_ring_buffers = RX_RING_BUFFERS;
+	printk(KERN_DEBUG "c101: using %u TX + %u RX packets rings\n",
+	       card->tx_ring_buffers, card->rx_ring_buffers);
 
 	card->buff_offset = C101_WINDOW_SIZE; /* Bytes 1D00-1FFF reserved */
 
@@ -337,7 +354,7 @@
 		return -ENOSYS;	/* no parameters specified, abort */
 	}
 
-	printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
+	printk(KERN_INFO "%s\n", version);
 
 	do {
 		unsigned long irq, ram;
@@ -352,7 +369,7 @@
 			c101_run(irq, ram);
 
 		if (*hw == '\x0')
-			return 0;
+			return first_card ? 0 : -ENOSYS;
 	}while(*hw++ == ':');
 
 	printk(KERN_ERR "c101: invalid hardware parameters\n");
@@ -360,17 +377,6 @@
 }
 
 
-#ifndef MODULE
-static int __init c101_setup(char *str)
-{
-	hw = str;
-	return 1;
-}
-
-__setup("c101=", c101_setup);
-#endif
-
-
 static void __exit c101_cleanup(void)
 {
 	card_t *card = first_card;
@@ -389,5 +395,5 @@
 
 MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
 MODULE_DESCRIPTION("Moxa C101 serial port driver");
-MODULE_LICENSE("GPL");
-MODULE_PARM(hw, "s");		/* hw=irq,ram:irq,... */
+MODULE_LICENSE("GPL v2");
+module_param(hw, charp, 0444);	/* hw=irq,ram:irq,... */
diff -urN linux-2.5.66-bk13/drivers/net/wan/dscc4.c linux-2.5.67/drivers/net/wan/dscc4.c
--- linux-2.5.66-bk13/drivers/net/wan/dscc4.c	2003-03-24 14:00:10.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/dscc4.c	2003-04-07 10:55:17.000000000 -0700
@@ -620,7 +620,7 @@
 		skb->tail += pkt_len;
 		skb->len = pkt_len;
        	if (netif_running(dev))
-			skb->protocol = htons(ETH_P_HDLC);
+			skb->protocol = hdlc_type_trans(skb, dev);
 		skb->dev->last_rx = jiffies;
 		netif_rx(skb);
 	} else {
diff -urN linux-2.5.66-bk13/drivers/net/wan/farsync.c linux-2.5.67/drivers/net/wan/farsync.c
--- linux-2.5.66-bk13/drivers/net/wan/farsync.c	2003-03-24 14:01:11.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/farsync.c	2003-04-07 10:55:17.000000000 -0700
@@ -761,7 +761,7 @@
         /* Push upstream */
         skb->mac.raw = skb->data;
         skb->dev = hdlc_to_dev ( &port->hdlc );
-        skb->protocol = htons ( ETH_P_HDLC );
+        skb->protocol = hdlc_type_trans(skb, skb->dev);
         netif_rx ( skb );
 
         port_to_dev ( port )->last_rx = jiffies;
diff -urN linux-2.5.66-bk13/drivers/net/wan/hd6457x.c linux-2.5.67/drivers/net/wan/hd6457x.c
--- linux-2.5.66-bk13/drivers/net/wan/hd6457x.c	2003-03-24 14:00:09.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hd6457x.c	2003-04-07 10:55:17.000000000 -0700
@@ -1,16 +1,29 @@
 /*
  * Hitachi SCA HD64570 and HD64572 common driver for Linux
  *
- * Copyright (C) 1998-2000 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  *
  * Sources of information:
  *    Hitachi HD64570 SCA User's Manual
  *    Hitachi HD64572 SCA-II User's Manual
+ *
+ * We use the following SCA memory map:
+ *
+ * Packet buffer descriptor rings - starting from winbase or win0base:
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #0 RX ring
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #0 TX ring
+ * rx_ring_buffers * sizeof(pkt_desc) = logical channel #1 RX ring (if used)
+ * tx_ring_buffers * sizeof(pkt_desc) = logical channel #1 TX ring (if used)
+ *
+ * Packet data buffers - starting from winbase + buff_offset:
+ * rx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 RX buffers
+ * tx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 TX buffers
+ * rx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 RX buffers (if used)
+ * tx_ring_buffers * HDLC_MAX_MRU     = logical channel #0 TX buffers (if used)
  */
 
 #include <linux/module.h>
@@ -42,8 +55,6 @@
 #error Either hd64570.h or hd64572.h must be included
 #endif
 
-static char sca_version[]="1.09";
-
 #define get_msci(port)	  (phy_node(port) ?   MSCI1_OFFSET :   MSCI0_OFFSET)
 #define get_dmac_rx(port) (phy_node(port) ? DMAC1RX_OFFSET : DMAC0RX_OFFSET)
 #define get_dmac_tx(port) (phy_node(port) ? DMAC1TX_OFFSET : DMAC0TX_OFFSET)
@@ -116,24 +127,35 @@
 
 
 
-static inline u8 next_desc(port_t *port, u8 desc)
+static inline u16 next_desc(port_t *port, u16 desc, int transmit)
+{
+	return (desc + 1) % (transmit ? port_to_card(port)->tx_ring_buffers
+			     : port_to_card(port)->rx_ring_buffers);
+}
+
+
+
+static inline u16 desc_abs_number(port_t *port, u16 desc, int transmit)
 {
-	return (desc + 1) % port_to_card(port)->ring_buffers;
+	u16 rx_buffs = port_to_card(port)->rx_ring_buffers;
+	u16 tx_buffs = port_to_card(port)->tx_ring_buffers;
+
+	desc %= (transmit ? tx_buffs : rx_buffs); // called with "X + 1" etc.
+	return log_node(port) * (rx_buffs + tx_buffs) +
+		transmit * rx_buffs + desc;
 }
 
 
 
-static inline u16 desc_offset(port_t *port, u8 desc, u8 transmit)
+static inline u16 desc_offset(port_t *port, u16 desc, int transmit)
 {
 	/* Descriptor offset always fits in 16 bytes */
-	u8 buffs = port_to_card(port)->ring_buffers;
-	return ((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) *
-		sizeof(pkt_desc);
+	return desc_abs_number(port, desc, transmit) * sizeof(pkt_desc);
 }
 
 
 
-static inline pkt_desc* desc_address(port_t *port, u8 desc, u8 transmit)
+static inline pkt_desc* desc_address(port_t *port, u16 desc, int transmit)
 {
 #ifdef PAGE0_ALWAYS_MAPPED
 	return (pkt_desc*)(win0base(port_to_card(port))
@@ -146,12 +168,10 @@
 
 
 
-static inline u32 buffer_offset(port_t *port, u8 desc, u8 transmit)
+static inline u32 buffer_offset(port_t *port, u16 desc, int transmit)
 {
-	u8 buffs = port_to_card(port)->ring_buffers;
 	return port_to_card(port)->buff_offset +
-		((log_node(port) * 2 + transmit) * buffs + (desc % buffs)) *
-		(u32)HDLC_MAX_MRU;
+		desc_abs_number(port, desc, transmit) * (u32)HDLC_MAX_MRU;
 }
 
 
@@ -159,8 +179,7 @@
 static void sca_init_sync_port(port_t *port)
 {
 	card_t *card = port_to_card(port);
-	u8 transmit, i;
-	u16 dmac, buffs = card->ring_buffers;
+	int transmit, i;
 
 	port->rxin = 0;
 	port->txin = 0;
@@ -171,6 +190,10 @@
 #endif
 
 	for (transmit = 0; transmit < 2; transmit++) {
+		u16 dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
+		u16 buffs = transmit ? card->tx_ring_buffers
+			: card->rx_ring_buffers;
+
 		for (i = 0; i < buffs; i++) {
 			pkt_desc* desc = desc_address(port, i, transmit);
 			u16 chain_off = desc_offset(port, i + 1, transmit);
@@ -182,7 +205,6 @@
 			writeb(0, &desc->stat);
 		}
 
-		dmac = transmit ? get_dmac_tx(port) : get_dmac_rx(port);
 		/* DMA disable - to halt state */
 		sca_out(0, transmit ? DSR_TX(phy_node(port)) :
 			DSR_RX(phy_node(port)), card);
@@ -247,7 +269,7 @@
 
 
 
-static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u8 rxin)
+static inline void sca_rx(card_t *card, port_t *port, pkt_desc *desc, u16 rxin)
 {
 	struct sk_buff *skb;
 	u16 len;
@@ -294,7 +316,7 @@
 	skb->mac.raw = skb->data;
 	skb->dev = hdlc_to_dev(&port->hdlc);
 	skb->dev->last_rx = jiffies;
-	skb->protocol = htons(ETH_P_HDLC);
+	skb->protocol = hdlc_type_trans(skb, hdlc_to_dev(&port->hdlc));
 	netif_rx(skb);
 }
 
@@ -341,7 +363,7 @@
 
 		/* Set new error descriptor address */
 		sca_outa(desc_off, dmac + EDAL, card);
-		port->rxin = next_desc(port, port->rxin);
+		port->rxin = next_desc(port, port->rxin, 0);
 	}
 
 	/* make sure RX DMA is enabled */
@@ -377,8 +399,7 @@
 		port->hdlc.stats.tx_packets++;
 		port->hdlc.stats.tx_bytes += readw(&desc->len);
 		writeb(0, &desc->stat);	/* Free descriptor */
-		port->txlast = (port->txlast + 1) %
-			port_to_card(port)->ring_buffers;
+		port->txlast = next_desc(port, port->txlast, 1);
 	}
 
 	netif_wake_queue(hdlc_to_dev(&port->hdlc));
@@ -390,8 +411,6 @@
 static void sca_intr(int irq, void* dev_id, struct pt_regs *regs)
 {
 	card_t *card = dev_id;
-/* Maximum events to handle at each interrupt - should I increase it? */
-	int boguscnt = 4;
 	int i;
 	u8 stat;
 
@@ -412,19 +431,9 @@
 				if (stat & SCA_INTR_DMAC_TX(i))
 					sca_tx_intr(port);
 			}
-
-			if (--boguscnt < 0) {
-#if 0
-				printk(KERN_ERR "%s: too much work at "
-				       "interrupt\n",
-				       hdlc_to_name(&port->hdlc));
-#endif
-				goto exit;
-			}
 		}
 	}
 
- exit:
 #ifndef ALL_PAGES_ALWAYS_MAPPED
 	openwin(card, page);		/* Restore original page */
 #endif
@@ -435,7 +444,7 @@
 static void sca_set_port(port_t *port)
 {
 	card_t* card = port_to_card(port);
-	u8 msci = get_msci(port);
+	u16 msci = get_msci(port);
 	u8 md2 = sca_in(msci + MD2, card);
 	unsigned int tmc, br = 10, brv = 1024;
 
@@ -495,7 +504,7 @@
 {
 	port_t *port = hdlc_to_port(hdlc);
 	card_t* card = port_to_card(port);
-	u8 msci = get_msci(port);
+	u16 msci = get_msci(port);
 	u8 md0, md2;
 
 	switch(port->encoding) {
@@ -516,7 +525,7 @@
 	case PARITY_CRC16_PR0_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU_0; break;
 #else
 	case PARITY_CRC32_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU32; break;
-#endif	
+#endif
 	case PARITY_CRC16_PR1_CCITT: md0 = MD0_HDLC | MD0_CRC_ITU;   break;
 	default:		     md0 = MD0_HDLC | MD0_CRC_NONE;
 	}
@@ -613,7 +622,7 @@
 	    parity != PARITY_CRC16_PR0_CCITT &&
 #else
 	    parity != PARITY_CRC32_PR1_CCITT &&
-#endif	
+#endif
 	    parity != PARITY_CRC16_PR1_CCITT)
 		return -EINVAL;
 
@@ -639,14 +648,13 @@
 	openwin(card, 0);
 #endif
 
-	printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u "
-	       "%sactive",
+	printk(KERN_ERR "RX ring: CDA=%u EDA=%u DSR=%02X in=%u %sactive",
 	       sca_ina(get_dmac_rx(port) + CDAL, card),
 	       sca_ina(get_dmac_rx(port) + EDAL, card),
 	       sca_in(DSR_RX(phy_node(port)), card),
 	       port->rxin,
 	       sca_in(DSR_RX(phy_node(port)), card) & DSR_DE?"":"in");
-	for (cnt = 0; cnt<port_to_card(port)->ring_buffers; cnt++)
+	for (cnt = 0; cnt < port_to_card(port)->rx_ring_buffers; cnt++)
 		printk(" %02X",
 		       readb(&(desc_address(port, cnt, 0)->stat)));
 
@@ -658,7 +666,7 @@
 	       port->txlast,
 	       sca_in(DSR_TX(phy_node(port)), card) & DSR_DE ? "" : "in");
 
-	for (cnt = 0; cnt<port_to_card(port)->ring_buffers; cnt++)
+	for (cnt = 0; cnt < port_to_card(port)->tx_ring_buffers; cnt++)
 		printk(" %02X",
 		       readb(&(desc_address(port, cnt, 1)->stat)));
 	printk("\n");
@@ -750,7 +758,7 @@
 	writeb(ST_TX_EOM, &desc->stat);
 	dev->trans_start = jiffies;
 
-	port->txin = next_desc(port, port->txin);
+	port->txin = next_desc(port, port->txin, 1);
 	sca_outa(desc_offset(port, port->txin, 1),
 		 get_dmac_tx(port) + EDAL, card);
 
@@ -767,7 +775,49 @@
 }
 
 
-static void sca_init(card_t *card, int wait_states)
+
+#ifdef NEED_DETECT_RAM
+static u32 __devinit sca_detect_ram(card_t *card, u8 *rambase, u32 ramsize)
+{
+	/* Round RAM size to 32 bits, fill from end to start */
+	u32 i = ramsize &= ~3;
+
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+	u32 size = winsize(card);
+
+	openwin(card, (i - 4) / size); /* select last window */
+#endif
+	do {
+		i -= 4;
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+		if ((i + 4) % size == 0)
+			openwin(card, i / size);
+		writel(i ^ 0x12345678, rambase + i % size);
+#else
+		writel(i ^ 0x12345678, rambase + i);
+#endif
+	}while (i > 0);
+
+	for (i = 0; i < ramsize ; i += 4) {
+#ifndef ALL_PAGES_ALWAYS_MAPPED
+		if (i % size == 0)
+			openwin(card, i / size);
+
+		if (readl(rambase + i % size) != (i ^ 0x12345678))
+			break;
+#else
+		if (readl(rambase + i) != (i ^ 0x12345678))
+			break;
+#endif
+	}
+
+	return i;
+}
+#endif /* NEED_DETECT_RAM */
+
+
+
+static void __devinit sca_init(card_t *card, int wait_states)
 {
 	sca_out(wait_states, WCRL, card); /* Wait Control */
 	sca_out(wait_states, WCRM, card);
diff -urN linux-2.5.66-bk13/drivers/net/wan/hdlc_cisco.c linux-2.5.67/drivers/net/wan/hdlc_cisco.c
--- linux-2.5.66-bk13/drivers/net/wan/hdlc_cisco.c	2003-03-24 14:00:15.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hdlc_cisco.c	2003-04-07 10:55:17.000000000 -0700
@@ -2,12 +2,11 @@
  * Generic HDLC support routines for Linux
  * Cisco HDLC support
  *
- * Copyright (C) 2000 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  */
 
 #include <linux/config.h>
@@ -80,17 +79,43 @@
 	data->par1 = htonl(par1);
 	data->par2 = htonl(par2);
 	data->rel = 0xFFFF;
-	data->time = htonl(jiffies * 1000 / HZ);
+	/* we will need do_div here if 1000 % HZ != 0 */
+	data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ));
 
 	skb_put(skb, sizeof(cisco_packet));
 	skb->priority = TC_PRIO_CONTROL;
 	skb->dev = hdlc_to_dev(hdlc);
+	skb->nh.raw = skb->data;
 
 	dev_queue_xmit(skb);
 }
 
 
 
+static unsigned short cisco_type_trans(struct sk_buff *skb,
+				       struct net_device *dev)
+{
+	hdlc_header *data = (hdlc_header*)skb->data;
+
+	if (skb->len < sizeof(hdlc_header))
+		return __constant_htons(ETH_P_HDLC);
+
+	if (data->address != CISCO_MULTICAST &&
+	    data->address != CISCO_UNICAST)
+		return __constant_htons(ETH_P_HDLC);
+
+	switch(data->protocol) {
+	case __constant_htons(ETH_P_IP):
+	case __constant_htons(ETH_P_IPX):
+	case __constant_htons(ETH_P_IPV6):
+		skb_pull(skb, sizeof(hdlc_header));
+		return data->protocol;
+	default:
+		return __constant_htons(ETH_P_HDLC);
+	}
+}
+
+
 static void cisco_rx(struct sk_buff *skb)
 {
 	hdlc_device *hdlc = dev_to_hdlc(skb->dev);
@@ -109,14 +134,6 @@
 	skb_pull(skb, sizeof(hdlc_header));
 
 	switch(ntohs(data->protocol)) {
-	case ETH_P_IP:
-	case ETH_P_IPX:
-	case ETH_P_IPV6:
-		skb->protocol = data->protocol;
-		skb->dev = hdlc_to_dev(hdlc);
-		netif_rx(skb);
-		return;
-
 	case CISCO_SYS_INFO:
 		/* Packet is not needed, drop it. */
 		dev_kfree_skb_any(skb);
@@ -288,6 +305,7 @@
 		hdlc->open = cisco_open;
 		hdlc->stop = cisco_close;
 		hdlc->netif_rx = cisco_rx;
+		hdlc->type_trans = cisco_type_trans;
 		hdlc->proto = IF_PROTO_CISCO;
 		dev->hard_start_xmit = hdlc->xmit;
 		dev->hard_header = cisco_hard_header;
diff -urN linux-2.5.66-bk13/drivers/net/wan/hdlc_fr.c linux-2.5.67/drivers/net/wan/hdlc_fr.c
--- linux-2.5.66-bk13/drivers/net/wan/hdlc_fr.c	2003-03-24 14:01:18.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hdlc_fr.c	2003-04-07 10:55:17.000000000 -0700
@@ -2,13 +2,22 @@
  * Generic HDLC support routines for Linux
  * Frame Relay support
  *
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
- */
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+
+ Theory of PVC state in DCE mode:
+
+ (exist,new) -> 0,0 when "PVC create" or if "link unreliable"
+         0,x -> 1,1 if "link reliable" when sending FULL STATUS
+         1,1 -> 1,0 if received FULL STATUS ACK
+
+ (active)    -> 0 when "ifconfig PVC down" or "link unreliable" or "PVC create"
+             -> 1 when "PVC up" and (exist,new) = 1,0
+*/
 
 #include <linux/config.h>
 #include <linux/module.h>
@@ -20,19 +29,23 @@
 #include <linux/init.h>
 #include <linux/skbuff.h>
 #include <linux/pkt_sched.h>
+#include <linux/random.h>
 #include <linux/inetdevice.h>
 #include <linux/lapb.h>
 #include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
 #include <linux/hdlc.h>
 
 
 __inline__ pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
 {
-	pvc_device *pvc=hdlc->state.fr.first_pvc;
-	
-	while (pvc) {
-		if (netdev_dlci(&pvc->netdev) == dlci)
+	pvc_device *pvc = hdlc->state.fr.first_pvc;
+
+	while(pvc) {
+		if (pvc->dlci == dlci)
 			return pvc;
+		if (pvc->dlci > dlci)
+			return NULL; /* the listed is sorted */
 		pvc = pvc->next;
 	}
 
@@ -40,18 +53,72 @@
 }
 
 
+__inline__ pvc_device* add_pvc(hdlc_device *hdlc, u16 dlci)
+{
+	pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc;
+
+	while(*pvc_p) {
+		if ((*pvc_p)->dlci == dlci)
+			return *pvc_p;
+		if ((*pvc_p)->dlci > dlci)
+			break;	/* the listed is sorted */
+		pvc_p = &(*pvc_p)->next;
+	}
+
+	pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC);
+	if (!pvc)
+		return NULL;
+
+	memset(pvc, 0, sizeof(pvc_device));
+	pvc->dlci = dlci;
+	pvc->master = hdlc;
+	pvc->next = *pvc_p;	/* Put it in the chain */
+	*pvc_p = pvc;
+	return pvc;
+}
+
+
+__inline__ int pvc_is_used(pvc_device *pvc)
+{
+	return pvc->main != NULL || pvc->ether != NULL;
+}
+
+
+__inline__ void delete_unused_pvcs(hdlc_device *hdlc)
+{
+	pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+
+	while(*pvc_p) {
+		if (!pvc_is_used(*pvc_p)) {
+			pvc_device *pvc = *pvc_p;
+			*pvc_p = pvc->next;
+			kfree(pvc);
+			continue;
+		}
+		pvc_p = &(*pvc_p)->next;
+	}
+}
 
-__inline__ u16 status_to_dlci(hdlc_device *hdlc, u8 *status,
-			      int *active, int *new)
+
+__inline__ struct net_device** get_dev_p(pvc_device *pvc, int type)
 {
-	*new = (status[2] & 0x08);
-	*active = (!*new && (status[2] & 0x02));
+	if (type == ARPHRD_ETHER)
+		return &pvc->ether;
+	else
+		return &pvc->main;
+}
+
+
+__inline__ u16 status_to_dlci(u8 *status, int *active, int *new)
+{
+	*new = (status[2] & 0x08) ? 1 : 0;
+	*active = (status[2] & 0x02) ? 1 : 0;
 
 	return ((status[0] & 0x3F)<<4) | ((status[1] & 0x78)>>3);
 }
 
 
-__inline__ void dlci_to_status(hdlc_device *hdlc, u16 dlci, u8 *status,
+__inline__ void dlci_to_status(u16 dlci, u8 *status,
 			       int active, int new)
 {
 	status[0] = (dlci>>4) & 0x3F;
@@ -66,37 +133,50 @@
 
 
 
-static int fr_hard_header(struct sk_buff *skb, struct net_device *dev,
-			  u16 type, void *daddr, void *saddr, unsigned int len)
+static int fr_hard_header(struct sk_buff **skb_p, u16 dlci)
 {
 	u16 head_len;
+	struct sk_buff *skb = *skb_p;
 
-	if (!daddr)
-		daddr = dev->broadcast;
-
-#ifdef CONFIG_HDLC_DEBUG_HARD_HEADER
-	printk(KERN_DEBUG "%s: fr_hard_header called\n", dev->name);
-#endif
-
-	switch(type) {
-	case ETH_P_IP:
+	switch(skb->protocol) {
+	case __constant_ntohs(ETH_P_IP):
 		head_len = 4;
 		skb_push(skb, head_len);
 		skb->data[3] = NLPID_IP;
 		break;
 
-	case ETH_P_IPV6:
+	case __constant_ntohs(ETH_P_IPV6):
 		head_len = 4;
 		skb_push(skb, head_len);
 		skb->data[3] = NLPID_IPV6;
 		break;
 
-	case LMI_PROTO:
+	case __constant_ntohs(LMI_PROTO):
 		head_len = 4;
 		skb_push(skb, head_len);
 		skb->data[3] = LMI_PROTO;
 		break;
 
+	case __constant_ntohs(ETH_P_802_3):
+		head_len = 10;
+		if (skb_headroom(skb) < head_len) {
+			struct sk_buff *skb2 = skb_realloc_headroom(skb,
+								    head_len);
+			if (!skb2)
+				return -ENOBUFS;
+			dev_kfree_skb(skb);
+			skb = *skb_p = skb2;
+		}
+		skb_push(skb, head_len);
+		skb->data[3] = FR_PAD;
+		skb->data[4] = NLPID_SNAP;
+		skb->data[5] = FR_PAD;
+		skb->data[6] = 0x80;
+		skb->data[7] = 0xC2;
+		skb->data[8] = 0x00;
+		skb->data[9] = 0x07; /* bridged Ethernet frame w/out FCS */
+		break;
+
 	default:
 		head_len = 10;
 		skb_push(skb, head_len);
@@ -105,14 +185,12 @@
 		skb->data[5] = FR_PAD;
 		skb->data[6] = FR_PAD;
 		skb->data[7] = FR_PAD;
-		skb->data[8] = type>>8;
-		skb->data[9] = (u8)type;
+		*(u16*)(skb->data + 8) = skb->protocol;
 	}
 
-	memcpy(skb->data, daddr, 2);
+	dlci_to_q922(skb->data, dlci);
 	skb->data[2] = FR_UI;
-
-	return head_len;
+	return 0;
 }
 
 
@@ -124,13 +202,12 @@
 	if ((hdlc_to_dev(pvc->master)->flags & IFF_UP) == 0)
 		return -EIO;  /* Master must be UP in order to activate PVC */
 
-	if (pvc->master->state.fr.settings.lmi != LMI_NONE)
-		pvc->state.active = 0;
-	else
-		pvc->state.active = 1;
+	if (pvc->open_count++ == 0) {
+		if (pvc->master->state.fr.settings.lmi == LMI_NONE)
+			pvc->state.active = 1;
 
-	pvc->state.new = 0;
-	pvc->master->state.fr.changed = 1;
+		pvc->master->state.fr.dce_changed = 1;
+	}
 	return 0;
 }
 
@@ -139,38 +216,94 @@
 static int pvc_close(struct net_device *dev)
 {
 	pvc_device *pvc = dev_to_pvc(dev);
-	pvc->state.active = pvc->state.new = 0;
-	pvc->master->state.fr.changed = 1;
+
+	if (--pvc->open_count == 0) {
+		if (pvc->master->state.fr.settings.lmi == LMI_NONE)
+			pvc->state.active = 0;
+
+		if (pvc->master->state.fr.settings.dce) {
+			pvc->master->state.fr.dce_changed = 1;
+			pvc->state.active = 0;
+		}
+	}
 	return 0;
 }
 
 
 
-static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
+int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
 	pvc_device *pvc = dev_to_pvc(dev);
+	fr_proto_pvc_info info;
 
-	if (pvc->state.active) {
-		skb->dev = hdlc_to_dev(pvc->master);
-		pvc->stats.tx_bytes += skb->len;
-		pvc->stats.tx_packets++;
-		if (pvc->state.fecn)
-			pvc->stats.tx_compressed++; /* TX Congestion counter */
-		dev_queue_xmit(skb);
-	} else {
-		pvc->stats.tx_dropped++;
-		dev_kfree_skb(skb);
+	if (ifr->ifr_settings.type == IF_GET_PROTO) {
+		if (dev->type == ARPHRD_ETHER)
+			ifr->ifr_settings.type = IF_PROTO_FR_ETH_PVC;
+		else
+			ifr->ifr_settings.type = IF_PROTO_FR_PVC;
+
+		if (ifr->ifr_settings.size < sizeof(info)) {
+			/* data size wanted */
+			ifr->ifr_settings.size = sizeof(info);
+			return -ENOBUFS;
+		}
+
+		info.dlci = pvc->dlci;
+		memcpy(info.master, hdlc_to_name(pvc->master), IFNAMSIZ);
+		if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info,
+				 &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
 	}
 
-	return 0;
+	return -EINVAL;
+}
+
+
+__inline__ struct net_device_stats *pvc_get_stats(struct net_device *dev)
+{
+	return (struct net_device_stats *)
+		((char *)dev + sizeof(struct net_device));
 }
 
 
 
-static struct net_device_stats *pvc_get_stats(struct net_device *dev)
+static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
 {
 	pvc_device *pvc = dev_to_pvc(dev);
-	return &pvc->stats;
+	struct net_device_stats *stats = pvc_get_stats(dev);
+
+	if (pvc->state.active) {
+		if (dev->type == ARPHRD_ETHER) {
+			int pad = ETH_ZLEN - skb->len;
+			if (pad > 0) { /* Pad the frame with zeros */
+				int len = skb->len;
+				if (skb_tailroom(skb) < pad)
+					if (pskb_expand_head(skb, 0, pad,
+							     GFP_ATOMIC)) {
+						stats->tx_dropped++;
+						dev_kfree_skb(skb);
+						return 0;
+					}
+				skb_put(skb, pad);
+				memset(skb->data + len, 0, pad);
+			}
+			skb->protocol = __constant_htons(ETH_P_802_3);
+		}
+		if (!fr_hard_header(&skb, pvc->dlci)) {
+			stats->tx_bytes += skb->len;
+			stats->tx_packets++;
+			if (pvc->state.fecn) /* TX Congestion counter */
+				stats->tx_compressed++;
+			skb->dev = hdlc_to_dev(pvc->master);
+			dev_queue_xmit(skb);
+			return 0;
+		}
+	}
+
+	stats->tx_dropped++;
+	dev_kfree_skb(skb);
+	return 0;
 }
 
 
@@ -187,9 +320,15 @@
 
 static inline void fr_log_dlci_active(pvc_device *pvc)
 {
-	printk(KERN_INFO "%s: %sactive%s\n", pvc_to_name(pvc),
-	       pvc->state.active ? "" : "in",
-	       pvc->state.new ? " new" : "");
+	printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n",
+	       hdlc_to_name(pvc->master),
+	       pvc->dlci,
+	       pvc->main ? pvc->main->name : "",
+	       pvc->main && pvc->ether ? " " : "",
+	       pvc->ether ? pvc->ether->name : "",
+	       pvc->state.new ? " new" : "",
+	       !pvc->state.exist ? "deleted" :
+	       pvc->state.active ? "active" : "inactive");
 }
 
 
@@ -213,8 +352,8 @@
 	int i = 0;
 
 	if (hdlc->state.fr.settings.dce && fullrep) {
-		len += hdlc->state.fr.pvc_count * (2 + stat_len);
-		if (len > HDLC_MAX_MTU) {
+		len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
+		if (len > HDLC_MAX_MRU) {
 			printk(KERN_WARNING "%s: Too many PVCs while sending "
 			       "LMI full report\n", hdlc_to_name(hdlc));
 			return;
@@ -224,12 +363,13 @@
 	skb = dev_alloc_skb(len);
 	if (!skb) {
 		printk(KERN_WARNING "%s: Memory squeeze on fr_lmi_send()\n",
-			       hdlc_to_name(hdlc));
+		       hdlc_to_name(hdlc));
 		return;
 	}
 	memset(skb->data, 0, len);
 	skb_reserve(skb, 4);
-	fr_hard_header(skb, hdlc_to_dev(hdlc), LMI_PROTO, NULL, NULL, 0);
+	skb->protocol = __constant_htons(LMI_PROTO);
+	fr_hard_header(&skb, LMI_DLCI);
 	data = skb->tail;
 	data[i++] = LMI_CALLREF;
 	data[i++] = hdlc->state.fr.settings.dce
@@ -253,16 +393,20 @@
 				? LMI_CCITT_PVCSTAT : LMI_PVCSTAT;
 			data[i++] = stat_len;
 
-			if (hdlc->state.fr.reliable &&
-			    (pvc->netdev.flags & IFF_UP) &&
-			    !pvc->state.active &&
-			    !pvc->state.new) {
-				pvc->state.new = 1;
+			/* LMI start/restart */
+			if (hdlc->state.fr.reliable && !pvc->state.exist) {
+				pvc->state.exist = pvc->state.new = 1;
+				fr_log_dlci_active(pvc);
+			}
+
+			/* ifconfig PVC up */
+			if (pvc->open_count && !pvc->state.active &&
+			    pvc->state.exist && !pvc->state.new) {
+				pvc->state.active = 1;
 				fr_log_dlci_active(pvc);
 			}
 
-			dlci_to_status(hdlc, netdev_dlci(&pvc->netdev),
-				       data + i,
+			dlci_to_status(pvc->dlci, data + i,
 				       pvc->state.active, pvc->state.new);
 			i += stat_len;
 			pvc = pvc->next;
@@ -272,6 +416,7 @@
 	skb_put(skb, i);
 	skb->priority = TC_PRIO_CONTROL;
 	skb->dev = hdlc_to_dev(hdlc);
+	skb->nh.raw = skb->data;
 
 	dev_queue_xmit(skb);
 }
@@ -312,10 +457,11 @@
 
 		if (reliable) {
 			hdlc->state.fr.n391cnt = 0; /* Request full status */
-			hdlc->state.fr.changed = 1;
+			hdlc->state.fr.dce_changed = 1;
 		} else {
 			while (pvc) {	/* Deactivate all PVCs */
-				pvc->state.new = pvc->state.active = 0;
+				pvc->state.exist = 0;
+				pvc->state.active = pvc->state.new = 0;
 				pvc = pvc->next;
 			}
 		}
@@ -346,7 +492,7 @@
 {
 	int stat_len;
 	pvc_device *pvc;
-	int reptype = -1, error;
+	int reptype = -1, error, no_ram;
 	u8 rxseq, txseq;
 	int i;
 
@@ -420,20 +566,18 @@
 			while (pvc) {
 				if (pvc->state.new) {
 					pvc->state.new = 0;
-					pvc->state.active = 1;
-					fr_log_dlci_active(pvc);
 
 /* Tell DTE that new PVC is now active */
-					hdlc->state.fr.changed = 1;
+					hdlc->state.fr.dce_changed = 1;
 				}
 				pvc = pvc->next;
 			}
 		}
 
-		if (hdlc->state.fr.changed) {
+		if (hdlc->state.fr.dce_changed) {
 			reptype = LMI_FULLREP;
 			hdlc->state.fr.fullrep_sent = 1;
-			hdlc->state.fr.changed = 0;
+			hdlc->state.fr.dce_changed = 0;
 		}
 
 		fr_lmi_send(hdlc, reptype == LMI_FULLREP ? 1 : 0);
@@ -449,13 +593,14 @@
 	pvc = hdlc->state.fr.first_pvc;
 
 	while (pvc) {
-		pvc->state.deleted = pvc->state.active; /* mark active PVCs */
+		pvc->state.deleted = 1;
 		pvc = pvc->next;
 	}
 
+	no_ram = 0;
 	while (skb->len >= i + 2 + stat_len) {
 		u16 dlci;
-		int active, new;
+		unsigned int active, new;
 
 		if (skb->data[i] != ((hdlc->state.fr.settings.lmi == LMI_CCITT)
 				     ? LMI_CCITT_PVCSTAT : LMI_PVCSTAT)) {
@@ -472,21 +617,28 @@
 		}
 		i++;
 
-		dlci = status_to_dlci(hdlc, skb->data + i, &active, &new);
-		pvc = find_pvc(hdlc, dlci);
+		dlci = status_to_dlci(skb->data + i, &active, &new);
+
+		pvc = add_pvc(hdlc, dlci);
+
+		if (!pvc && !no_ram) {
+			printk(KERN_WARNING
+			       "%s: Memory squeeze on fr_lmi_recv()\n",
+			       hdlc_to_name(hdlc));
+			no_ram = 1;
+		}
 
-		active |= new;
 		if (pvc) {
-			if (active && !pvc->state.active &&
-			    (pvc->netdev.flags & IFF_UP)) {
+			pvc->state.exist = 1;
+			pvc->state.deleted = 0;
+			if (active != pvc->state.active ||
+			    new != pvc->state.new ||
+			    !pvc->state.exist) {
+				pvc->state.new = new;
 				pvc->state.active = active;
 				fr_log_dlci_active(pvc);
 			}
-			pvc->state.deleted = 0;
 		}
-		else if (new)
-			printk(KERN_INFO "%s: new PVC available, DLCI=%u\n",
-			       hdlc_to_name(hdlc), dlci);
 
 		i += stat_len;
 	}
@@ -494,10 +646,10 @@
 	pvc = hdlc->state.fr.first_pvc;
 
 	while (pvc) {
-		if (pvc->state.deleted) {
+		if (pvc->state.deleted && pvc->state.exist) {
 			pvc->state.active = pvc->state.new = 0;
+			pvc->state.exist = 0;
 			fr_log_dlci_active(pvc);
-			pvc->state.deleted = 0;
 		}
 		pvc = pvc->next;
 	}
@@ -517,8 +669,9 @@
 	u8 *data = skb->data;
 	u16 dlci;
 	pvc_device *pvc;
+	struct net_device *dev = NULL;
 
-	if (skb->len<4 || fh->ea1 || data[2] != FR_UI)
+	if (skb->len <= 4 || fh->ea1 || data[2] != FR_UI)
 		goto rx_error;
 
 	dlci = q922_to_dlci(skb->data);
@@ -550,57 +703,39 @@
 		printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
 		       hdlc_to_name(hdlc), dlci);
 #endif
-		goto rx_error;
-	}
-
-	if ((pvc->netdev.flags & IFF_UP) == 0) {
-#ifdef CONFIG_HDLC_DEBUG_PKT
-		printk(KERN_INFO "%s: PVC for received frame's DLCI %d is down\n",
-		       hdlc_to_name(hdlc), dlci);
-#endif
-		goto rx_error;
+		dev_kfree_skb_any(skb);
+		return;
 	}
 
-	pvc->stats.rx_packets++; /* PVC traffic */
-	pvc->stats.rx_bytes += skb->len;
-
-	if (pvc->state.fecn != (fh->fecn ? PVC_STATE_FECN : 0)) {
+	if (pvc->state.fecn != fh->fecn) {
 #ifdef CONFIG_HDLC_DEBUG_ECN
-		printk(KERN_DEBUG "%s: FECN O%s\n", pvc_to_name(pvc),
-		       fh->fecn ? "N" : "FF");
+		printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", hdlc_to_name(pvc),
+		       dlci, fh->fecn ? "N" : "FF");
 #endif
 		pvc->state.fecn ^= 1;
 	}
 
-	if (pvc->state.becn != (fh->becn ? PVC_STATE_BECN : 0)) {
+	if (pvc->state.becn != fh->becn) {
 #ifdef CONFIG_HDLC_DEBUG_ECN
-		printk(KERN_DEBUG "%s: BECN O%s\n", pvc_to_name(pvc),
-		       fh->becn ? "N" : "FF");
+		printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", hdlc_to_name(pvc),
+		       dlci, fh->becn ? "N" : "FF");
 #endif
 		pvc->state.becn ^= 1;
 	}
 
-	if (pvc->state.becn)
-		pvc->stats.rx_compressed++;
-
-	skb->dev = &pvc->netdev;
 
 	if (data[3] == NLPID_IP) {
 		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+		dev = pvc->main;
 		skb->protocol = htons(ETH_P_IP);
-		netif_rx(skb);
-		return;
-	}
-
 
-	if (data[3] == NLPID_IPV6) {
+	} else if (data[3] == NLPID_IPV6) {
 		skb_pull(skb, 4); /* Remove 4-byte header (hdr, UI, NLPID) */
+		dev = pvc->main;
 		skb->protocol = htons(ETH_P_IPV6);
-		netif_rx(skb);
-		return;
-	}
 
-	if (data[3] == FR_PAD && data[4] == NLPID_SNAP && data[5] == FR_PAD) {
+	} else if (skb->len > 10 && data[3] == FR_PAD &&
+		   data[4] == NLPID_SNAP && data[5] == FR_PAD) {
 		u16 oui = ntohs(*(u16*)(data + 6));
 		u16 pid = ntohs(*(u16*)(data + 8));
 		skb_pull(skb, 10);
@@ -610,23 +745,39 @@
 		case ETH_P_IPX:
 		case ETH_P_IP:	/* a long variant */
 		case ETH_P_IPV6:
+			dev = pvc->main;
 			skb->protocol = htons(pid);
 			break;
 
+		case 0x80C20007: /* bridged Ethernet frame */
+			if ((dev = pvc->ether) != NULL)
+				skb->protocol = eth_type_trans(skb, dev);
+			break;
+
 		default:
 			printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
 			       "PID=%x\n", hdlc_to_name(hdlc), oui, pid);
 			dev_kfree_skb_any(skb);
 			return;
 		}
-
-		netif_rx(skb);
+	} else {
+		printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "
+		       "length = %i\n", hdlc_to_name(hdlc), data[3], skb->len);
+		dev_kfree_skb_any(skb);
 		return;
 	}
 
-	printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x\n",
-	       hdlc_to_name(hdlc), data[3]);
-	dev_kfree_skb_any(skb);
+	if (dev) {
+		struct net_device_stats *stats = pvc_get_stats(dev);
+		stats->rx_packets++; /* PVC traffic */
+		stats->rx_bytes += skb->len;
+		if (pvc->state.becn)
+			stats->rx_compressed++;
+		skb->dev = dev;
+		netif_rx(skb);
+	} else
+		dev_kfree_skb_any(skb);
+
 	return;
 
  rx_error:
@@ -641,7 +792,7 @@
 	if (hdlc->state.fr.settings.lmi != LMI_NONE) {
 		hdlc->state.fr.last_poll = 0;
 		hdlc->state.fr.reliable = 0;
-		hdlc->state.fr.changed = 1;
+		hdlc->state.fr.dce_changed = 1;
 		hdlc->state.fr.request = 0;
 		hdlc->state.fr.fullrep_sent = 0;
 		hdlc->state.fr.last_errors = 0xFFFFFFFF;
@@ -669,90 +820,119 @@
 	if (hdlc->state.fr.settings.lmi != LMI_NONE)
 		del_timer_sync(&hdlc->state.fr.timer);
 
-	while(pvc) {
-		dev_close(&pvc->netdev); /* Shutdown all PVCs for this FRAD */
+	while(pvc) {		/* Shutdown all PVCs for this FRAD */
+		if (pvc->main)
+			dev_close(pvc->main);
+		if (pvc->ether)
+			dev_close(pvc->ether);
+		pvc->state.active = pvc->state.new = pvc->state.fecn =
+			pvc->state.becn = 0;
+		pvc->state.exist = 0;
 		pvc = pvc->next;
 	}
 }
 
 
-
-static int fr_pvc(hdlc_device *hdlc, unsigned int dlci, int create)
+static int fr_add_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
 {
-	pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
-	pvc_device *pvc;
-	int result;
+	pvc_device *pvc = NULL;
+	struct net_device *dev;
+	int result, used;
+	char * prefix = "pvc%d";
 
-	if(dlci <= 0 || dlci >= 1024)
-		return -EINVAL;	/* Only 10 bits for DLCI, DLCI 0 reserved */
+	if (type == ARPHRD_ETHER)
+		prefix = "pvceth%d";
 
-	while(*pvc_p) {
-		if (netdev_dlci(&(*pvc_p)->netdev) == dlci)
-			break;
-		pvc_p = &(*pvc_p)->next;
+	if ((pvc = add_pvc(hdlc, dlci)) == NULL) {
+		printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
+		       hdlc_to_name(hdlc));
+		return -ENOBUFS;
 	}
 
-	if (create) {		/* Create PVC */
-		if (*pvc_p != NULL)
-			return -EEXIST;
-
-		pvc = *pvc_p = kmalloc(sizeof(pvc_device), GFP_KERNEL);
-		if (!pvc) {
-			printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
-			       hdlc_to_name(hdlc));
-			return -ENOBUFS;
-		}
-		memset(pvc, 0, sizeof(pvc_device));
+	if (*get_dev_p(pvc, type))
+		return -EEXIST;
 
-		pvc->netdev.hard_start_xmit = pvc_xmit;
-		pvc->netdev.get_stats = pvc_get_stats;
-		pvc->netdev.open = pvc_open;
-		pvc->netdev.stop = pvc_close;
-		pvc->netdev.change_mtu = pvc_change_mtu;
-		pvc->netdev.mtu = HDLC_MAX_MTU;
-
-		pvc->netdev.type = ARPHRD_DLCI;
-		pvc->netdev.hard_header_len = 16;
-		pvc->netdev.hard_header = fr_hard_header;
-		pvc->netdev.tx_queue_len = 0;
-		pvc->netdev.flags = IFF_POINTOPOINT;
-
-		pvc->master = hdlc;
-		*(u16*)pvc->netdev.dev_addr = htons(dlci);
-		dlci_to_q922(pvc->netdev.broadcast, dlci);
-		pvc->netdev.addr_len = 2;
+	used = pvc_is_used(pvc);
 
-		result = dev_alloc_name(&pvc->netdev, "pvc%d");
-		if (result < 0) {
-			kfree(pvc);
-			*pvc_p = NULL;
-			return result;
-		}
-
-		if (register_netdevice(&pvc->netdev) != 0) {
-			kfree(pvc);
-			*pvc_p = NULL;
-			return -EIO;
-		}
+	dev = kmalloc(sizeof(struct net_device) +
+		      sizeof(struct net_device_stats), GFP_KERNEL);
+	if (!dev) {
+		printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
+		       hdlc_to_name(hdlc));
+		delete_unused_pvcs(hdlc);
+		return -ENOBUFS;
+	}
+	memset(dev, 0, sizeof(struct net_device) +
+	       sizeof(struct net_device_stats));
 
-		hdlc->state.fr.changed = 1;
-		hdlc->state.fr.pvc_count++;
-		return 0;
+	if (type == ARPHRD_ETHER) {
+		ether_setup(dev);
+		memcpy(dev->dev_addr, "\x00\x01", 2);
+                get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
+	} else {
+		dev->type = ARPHRD_DLCI;
+		dev->flags = IFF_POINTOPOINT;
+		dev->hard_header_len = 10;
+		dev->addr_len = 2;
+		*(u16*)dev->dev_addr = htons(dlci);
+		dlci_to_q922(dev->broadcast, dlci);
+	}
+	dev->hard_start_xmit = pvc_xmit;
+	dev->get_stats = pvc_get_stats;
+	dev->open = pvc_open;
+	dev->stop = pvc_close;
+	dev->do_ioctl = pvc_ioctl;
+	dev->change_mtu = pvc_change_mtu;
+	dev->mtu = HDLC_MAX_MTU;
+	dev->tx_queue_len = 0;
+	dev->priv = pvc;
+
+	result = dev_alloc_name(dev, prefix);
+	if (result < 0) {
+		kfree(dev);
+		delete_unused_pvcs(hdlc);
+		return result;
+	}
+
+	if (register_netdevice(dev) != 0) {
+		kfree(dev);
+		delete_unused_pvcs(hdlc);
+		return -EIO;
+	}
+
+	*get_dev_p(pvc, type) = dev;
+	if (!used) {
+		hdlc->state.fr.dce_changed = 1;
+		hdlc->state.fr.dce_pvc_count++;
 	}
+	return 0;
+}
+
+
 
-	if (*pvc_p == NULL)		/* Delete PVC */
+static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
+{
+	pvc_device *pvc;
+	struct net_device *dev;
+
+	if ((pvc = find_pvc(hdlc, dlci)) == NULL)
 		return -ENOENT;
 
-	pvc = *pvc_p;
+	if ((dev = *get_dev_p(pvc, type)) == NULL)
+		return -ENOENT;
 
-	if (pvc->netdev.flags & IFF_UP)
+	if (dev->flags & IFF_UP)
 		return -EBUSY;		/* PVC in use */
 
-	hdlc->state.fr.changed = 1;
-	hdlc->state.fr.pvc_count--;
-	*pvc_p = pvc->next;
-	unregister_netdevice(&pvc->netdev);
-	kfree(pvc);
+	unregister_netdevice(dev);
+	kfree(dev);
+	*get_dev_p(pvc, type) = NULL;
+
+	if (!pvc_is_used(pvc)) {
+		hdlc->state.fr.dce_pvc_count--;
+		hdlc->state.fr.dce_changed = 1;
+	}
+	delete_unused_pvcs(hdlc);
 	return 0;
 }
 
@@ -763,14 +943,21 @@
 	pvc_device *pvc = hdlc->state.fr.first_pvc;
 	while(pvc) {
 		pvc_device *next = pvc->next;
-		unregister_netdev(&pvc->netdev);
+		if (pvc->main) {
+			unregister_netdevice(pvc->main);
+			kfree(pvc->main);
+		}
+		if (pvc->ether) {
+			unregister_netdevice(pvc->ether);
+			kfree(pvc->ether);
+		}
 		kfree(pvc);
 		pvc = next;
 	}
 
 	hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
-	hdlc->state.fr.pvc_count = 0;
-	hdlc->state.fr.changed = 1;
+	hdlc->state.fr.dce_pvc_count = 0;
+	hdlc->state.fr.dce_changed = 1;
 }
 
 
@@ -828,25 +1015,27 @@
 		if (hdlc->proto != IF_PROTO_FR) {
 			hdlc_proto_detach(hdlc);
 			hdlc->state.fr.first_pvc = NULL;
-			hdlc->state.fr.pvc_count = 0;
+			hdlc->state.fr.dce_pvc_count = 0;
 		}
 		memcpy(&hdlc->state.fr.settings, &new_settings, size);
 
 		hdlc->open = fr_open;
 		hdlc->stop = fr_close;
 		hdlc->netif_rx = fr_rx;
+		hdlc->type_trans = NULL;
 		hdlc->proto_detach = fr_destroy;
 		hdlc->proto = IF_PROTO_FR;
 		dev->hard_start_xmit = hdlc->xmit;
-		dev->hard_header = fr_hard_header;
+		dev->hard_header = NULL;
 		dev->type = ARPHRD_FRAD;
-		dev->addr_len = 2;
-		*(u16*)dev->dev_addr = htons(LMI_DLCI);
-		dlci_to_q922(dev->broadcast, LMI_DLCI);
+		dev->flags = IFF_POINTOPOINT | IFF_NOARP;
+		dev->addr_len = 0;
 		return 0;
 
 	case IF_PROTO_FR_ADD_PVC:
 	case IF_PROTO_FR_DEL_PVC:
+	case IF_PROTO_FR_ADD_ETH_PVC:
+	case IF_PROTO_FR_DEL_ETH_PVC:
 		if(!capable(CAP_NET_ADMIN))
 			return -EPERM;
 
@@ -854,8 +1043,20 @@
 				   sizeof(fr_proto_pvc)))
 			return -EFAULT;
 
-		return fr_pvc(hdlc, pvc.dlci,
-			      ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC);
+		if (pvc.dlci <= 0 || pvc.dlci >= 1024)
+			return -EINVAL;	/* Only 10 bits, DLCI 0 reserved */
+
+		if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC ||
+		    ifr->ifr_settings.type == IF_PROTO_FR_DEL_ETH_PVC)
+			result = ARPHRD_ETHER; /* bridged Ethernet device */
+		else
+			result = ARPHRD_DLCI;
+
+		if (ifr->ifr_settings.type == IF_PROTO_FR_ADD_PVC ||
+		    ifr->ifr_settings.type == IF_PROTO_FR_ADD_ETH_PVC)
+			return fr_add_pvc(hdlc, pvc.dlci, result);
+		else
+			return fr_del_pvc(hdlc, pvc.dlci, result);
 	}
 
 	return -EINVAL;
diff -urN linux-2.5.66-bk13/drivers/net/wan/hdlc_generic.c linux-2.5.67/drivers/net/wan/hdlc_generic.c
--- linux-2.5.66-bk13/drivers/net/wan/hdlc_generic.c	2003-03-24 14:00:01.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hdlc_generic.c	2003-04-07 10:55:17.000000000 -0700
@@ -1,17 +1,13 @@
 /*
  * Generic HDLC support routines for Linux
  *
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  *
- * Current status:
- *    - this is work in progress
- *    - not heavily tested on SMP
- *    - currently supported:
+ * Currently supported:
  *	* raw IP-in-HDLC
  *	* Cisco HDLC
  *	* Frame Relay with ANSI or CCITT LMI (both user and network side)
@@ -37,7 +33,7 @@
 #include <linux/hdlc.h>
 
 
-static const char* version = "HDLC support module revision 1.11";
+static const char* version = "HDLC support module revision 1.14";
 
 
 static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
@@ -60,7 +56,13 @@
 static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
 		    struct packet_type *p)
 {
-	dev_to_hdlc(dev)->netif_rx(skb);
+	hdlc_device *hdlc = dev_to_hdlc(dev);
+	if (hdlc->netif_rx)
+		hdlc->netif_rx(skb);
+	else {
+		hdlc->stats.rx_dropped++; /* Shouldn't happen */
+		dev_kfree_skb(skb);
+	}
 	return 0;
 }
 
@@ -69,6 +71,10 @@
 #define hdlc_raw_ioctl(hdlc, ifr)	-ENOSYS
 #endif
 
+#ifndef CONFIG_HDLC_RAW_ETH
+#define hdlc_raw_eth_ioctl(hdlc, ifr)	-ENOSYS
+#endif
+
 #ifndef CONFIG_HDLC_PPP
 #define hdlc_ppp_ioctl(hdlc, ifr)	-ENOSYS
 #endif
@@ -96,6 +102,7 @@
 
 	switch(ifr->ifr_settings.type) {
 	case IF_PROTO_HDLC:
+	case IF_PROTO_HDLC_ETH:
 	case IF_PROTO_PPP:
 	case IF_PROTO_CISCO:
 	case IF_PROTO_FR:
@@ -109,6 +116,7 @@
 
 	switch(proto) {
 	case IF_PROTO_HDLC:	return hdlc_raw_ioctl(hdlc, ifr);
+	case IF_PROTO_HDLC_ETH:	return hdlc_raw_eth_ioctl(hdlc, ifr);
 	case IF_PROTO_PPP:	return hdlc_ppp_ioctl(hdlc, ifr);
 	case IF_PROTO_CISCO:	return hdlc_cisco_ioctl(hdlc, ifr);
 	case IF_PROTO_FR:	return hdlc_fr_ioctl(hdlc, ifr);
@@ -144,7 +152,6 @@
 	if (result != 0)
 		return -EIO;
 
-	MOD_INC_USE_COUNT;
 	return 0;
 }
 
@@ -152,17 +159,17 @@
 
 void unregister_hdlc_device(hdlc_device *hdlc)
 {
+	rtnl_lock();
 	hdlc_proto_detach(hdlc);
-
-	unregister_netdev(hdlc_to_dev(hdlc));
-	MOD_DEC_USE_COUNT;
+	unregister_netdevice(hdlc_to_dev(hdlc));
+	rtnl_unlock();
 }
 
 
 
 MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
 MODULE_DESCRIPTION("HDLC support module");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
 
 EXPORT_SYMBOL(hdlc_ioctl);
 EXPORT_SYMBOL(register_hdlc_device);
diff -urN linux-2.5.66-bk13/drivers/net/wan/hdlc_ppp.c linux-2.5.67/drivers/net/wan/hdlc_ppp.c
--- linux-2.5.66-bk13/drivers/net/wan/hdlc_ppp.c	2003-03-24 13:59:58.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hdlc_ppp.c	2003-04-07 10:55:17.000000000 -0700
@@ -2,12 +2,11 @@
  * Generic HDLC support routines for Linux
  * Point-to-point protocol support
  *
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  */
 
 #include <linux/config.h>
@@ -68,10 +67,10 @@
 
 
 
-static void ppp_rx(struct sk_buff *skb)
+static unsigned short ppp_type_trans(struct sk_buff *skb,
+				     struct net_device *dev)
 {
-	skb->protocol = htons(ETH_P_WAN_PPP);
-	netif_rx(skb);
+	return __constant_htons(ETH_P_WAN_PPP);
 }
 
 
@@ -103,7 +102,8 @@
 
 		hdlc->open = ppp_open;
 		hdlc->stop = ppp_close;
-		hdlc->netif_rx = ppp_rx;
+		hdlc->netif_rx = NULL;
+		hdlc->type_trans = ppp_type_trans;
 		hdlc->proto = IF_PROTO_PPP;
 		dev->hard_start_xmit = hdlc->xmit;
 		dev->hard_header = NULL;
diff -urN linux-2.5.66-bk13/drivers/net/wan/hdlc_raw.c linux-2.5.67/drivers/net/wan/hdlc_raw.c
--- linux-2.5.66-bk13/drivers/net/wan/hdlc_raw.c	2003-03-24 14:00:49.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hdlc_raw.c	2003-04-07 10:55:17.000000000 -0700
@@ -2,12 +2,11 @@
  * Generic HDLC support routines for Linux
  * HDLC support
  *
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  */
 
 #include <linux/config.h>
@@ -26,10 +25,10 @@
 #include <linux/hdlc.h>
 
 
-static void raw_rx(struct sk_buff *skb)
+static unsigned short raw_type_trans(struct sk_buff *skb,
+				     struct net_device *dev)
 {
-	skb->protocol = htons(ETH_P_IP);
-	netif_rx(skb);
+	return __constant_htons(ETH_P_IP);
 }
 
 
@@ -67,7 +66,7 @@
 			new_settings.encoding = ENCODING_NRZ;
 
 		if (new_settings.parity == PARITY_DEFAULT)
-			new_settings.parity = PARITY_NONE;
+			new_settings.parity = PARITY_CRC16_PR1_CCITT;
 
 		result = hdlc->attach(hdlc, new_settings.encoding,
 				      new_settings.parity);
@@ -79,11 +78,13 @@
 
 		hdlc->open = NULL;
 		hdlc->stop = NULL;
-		hdlc->netif_rx = raw_rx;
+		hdlc->netif_rx = NULL;
+		hdlc->type_trans = raw_type_trans;
 		hdlc->proto = IF_PROTO_HDLC;
 		dev->hard_start_xmit = hdlc->xmit;
 		dev->hard_header = NULL;
 		dev->type = ARPHRD_RAWHDLC;
+		dev->flags = IFF_POINTOPOINT | IFF_NOARP;
 		dev->addr_len = 0;
 		return 0;
 	}
diff -urN linux-2.5.66-bk13/drivers/net/wan/hdlc_raw_eth.c linux-2.5.67/drivers/net/wan/hdlc_raw_eth.c
--- linux-2.5.66-bk13/drivers/net/wan/hdlc_raw_eth.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hdlc_raw_eth.c	2003-04-07 10:55:17.000000000 -0700
@@ -0,0 +1,110 @@
+/*
+ * Generic HDLC support routines for Linux
+ * HDLC Ethernet emulation support
+ *
+ * Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/errno.h>
+#include <linux/if_arp.h>
+#include <linux/init.h>
+#include <linux/skbuff.h>
+#include <linux/pkt_sched.h>
+#include <linux/random.h>
+#include <linux/inetdevice.h>
+#include <linux/lapb.h>
+#include <linux/rtnetlink.h>
+#include <linux/etherdevice.h>
+#include <linux/hdlc.h>
+
+
+static int eth_tx(struct sk_buff *skb, struct net_device *dev)
+{
+	int pad = ETH_ZLEN - skb->len;
+	if (pad > 0) {		/* Pad the frame with zeros */
+		int len = skb->len;
+		if (skb_tailroom(skb) < pad)
+			if (pskb_expand_head(skb, 0, pad, GFP_ATOMIC)) {
+				dev_to_hdlc(dev)->stats.tx_dropped++;
+				dev_kfree_skb(skb);
+				return 0;
+			}
+		skb_put(skb, pad);
+		memset(skb->data + len, 0, pad);
+	}
+	return dev_to_hdlc(dev)->xmit(skb, dev);
+}
+
+
+int hdlc_raw_eth_ioctl(hdlc_device *hdlc, struct ifreq *ifr)
+{
+	raw_hdlc_proto *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
+	const size_t size = sizeof(raw_hdlc_proto);
+	raw_hdlc_proto new_settings;
+	struct net_device *dev = hdlc_to_dev(hdlc);
+	int result;
+	void *old_ch_mtu;
+	int old_qlen;
+
+	switch (ifr->ifr_settings.type) {
+	case IF_GET_PROTO:
+		ifr->ifr_settings.type = IF_PROTO_HDLC_ETH;
+		if (ifr->ifr_settings.size < size) {
+			ifr->ifr_settings.size = size; /* data size wanted */
+			return -ENOBUFS;
+		}
+		if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+			return -EFAULT;
+		return 0;
+
+	case IF_PROTO_HDLC_ETH:
+		if (!capable(CAP_NET_ADMIN))
+			return -EPERM;
+
+		if (dev->flags & IFF_UP)
+			return -EBUSY;
+
+		if (copy_from_user(&new_settings, raw_s, size))
+			return -EFAULT;
+
+		if (new_settings.encoding == ENCODING_DEFAULT)
+			new_settings.encoding = ENCODING_NRZ;
+
+		if (new_settings.parity == PARITY_DEFAULT)
+			new_settings.parity = PARITY_CRC16_PR1_CCITT;
+
+		result = hdlc->attach(hdlc, new_settings.encoding,
+				      new_settings.parity);
+		if (result)
+			return result;
+
+		hdlc_proto_detach(hdlc);
+		memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
+
+		hdlc->open = NULL;
+		hdlc->stop = NULL;
+		hdlc->netif_rx = NULL;
+		hdlc->type_trans = eth_type_trans;
+		hdlc->proto = IF_PROTO_HDLC_ETH;
+		dev->hard_start_xmit = eth_tx;
+		old_ch_mtu = dev->change_mtu;
+		old_qlen = dev->tx_queue_len;
+		ether_setup(dev);
+		dev->change_mtu = old_ch_mtu;
+		dev->tx_queue_len = old_qlen;
+		memcpy(dev->dev_addr, "\x00\x01", 2);
+                get_random_bytes(dev->dev_addr + 2, ETH_ALEN - 2);
+		return 0;
+	}
+
+	return -EINVAL;
+}
diff -urN linux-2.5.66-bk13/drivers/net/wan/hdlc_x25.c linux-2.5.67/drivers/net/wan/hdlc_x25.c
--- linux-2.5.66-bk13/drivers/net/wan/hdlc_x25.c	2003-03-24 14:01:49.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/hdlc_x25.c	2003-04-07 10:55:17.000000000 -0700
@@ -2,12 +2,11 @@
  * Generic HDLC support routines for Linux
  * X.25 support
  *
- * Copyright (C) 1999 - 2001 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  */
 
 #include <linux/config.h>
@@ -204,6 +203,7 @@
 		hdlc->open = x25_open;
 		hdlc->stop = x25_close;
 		hdlc->netif_rx = x25_rx;
+		hdlc->type_trans = NULL;
 		hdlc->proto = IF_PROTO_X25;
 		dev->hard_start_xmit = x25_xmit;
 		dev->hard_header = NULL;
diff -urN linux-2.5.66-bk13/drivers/net/wan/n2.c linux-2.5.67/drivers/net/wan/n2.c
--- linux-2.5.66-bk13/drivers/net/wan/n2.c	2003-03-24 14:00:09.000000000 -0800
+++ linux-2.5.67/drivers/net/wan/n2.c	2003-04-07 10:55:17.000000000 -0700
@@ -1,12 +1,11 @@
 /*
  * SDL Inc. RISCom/N2 synchronous serial card driver for Linux
  *
- * Copyright (C) 1998-2002 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1998-2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  *
  * For information see http://hq.pm.waw.pl/hdlc/
  *
@@ -28,20 +27,28 @@
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/ioport.h>
+#include <linux/moduleparam.h>
 #include <linux/netdevice.h>
 #include <linux/hdlc.h>
 #include <asm/io.h>
 #include "hd64570.h"
 
 
-static const char* version = "SDL RISCom/N2 driver version: 1.10";
+static const char* version = "SDL RISCom/N2 driver version: 1.14";
 static const char* devname = "RISCom/N2";
 
 #define USE_WINDOWSIZE 16384
 #define USE_BUS16BITS 1
 #define CLOCK_BASE 9830400	/* 9.8304 MHz */
-
+#define MAX_PAGES      16	/* 16 RAM pages at max */
+#define MAX_RAM_SIZE 0x80000	/* 512 KB */
+#if MAX_RAM_SIZE > MAX_PAGES * USE_WINDOWSIZE
+#undef MAX_RAM_SIZE
+#define MAX_RAM_SIZE (MAX_PAGES * USE_WINDOWSIZE)
+#endif
 #define N2_IOPORTS 0x10
+#define NEED_DETECT_RAM
+#define MAX_TX_BUFFERS 10
 
 static char *hw = NULL;	/* pointer to hw=xxx command line string */
 
@@ -86,16 +93,16 @@
 	struct card_s *card;
 	spinlock_t lock;	/* TX lock */
 	sync_serial_settings settings;
+	int valid;		/* port enabled */
+	int rxpart;		/* partial frame received, next frame invalid*/
 	unsigned short encoding;
 	unsigned short parity;
+	u16 rxin;		/* rx ring buffer 'in' pointer */
+	u16 txin;		/* tx ring buffer 'in' and 'last' pointers */
+	u16 txlast;
 	u8 rxs, txs, tmc;	/* SCA registers */
-	u8 valid;		/* port enabled */
 	u8 phy_node;		/* physical port # - 0 or 1 */
 	u8 log_node;		/* logical port # */
-	u8 rxin;		/* rx ring buffer 'in' pointer */
-	u8 txin;		/* tx ring buffer 'in' and 'last' pointers */
-	u8 txlast;
-	u8 rxpart;		/* partial frame received, next frame invalid*/
 }port_t;
 
 
@@ -106,8 +113,9 @@
 	u32 ram_size;		/* number of bytes */
 	u16 io;			/* IO Base address */
 	u16 buff_offset;	/* offset of first buffer of first channel */
+	u16 rx_ring_buffers;	/* number of buffers in a ring */
+	u16 tx_ring_buffers;
 	u8 irq;			/* IRQ (3-15) */
-	u8 ring_buffers;	/* number of buffers in a ring */
 
 	port_t ports[2];
 	struct card_s *next_card;
@@ -209,14 +217,19 @@
 	int io = port->card->io;
 	u8 mcr = inb(io + N2_MCR) | (port->phy_node ? TX422_PORT1:TX422_PORT0);
 
+
+	if (!try_module_get(THIS_MODULE))
+		return -EFAULT;	/* rmmod in progress */
+
 	int result = hdlc_open(hdlc);
-	if (result)
+	if (result) {
 		return result;
+		module_put(THIS_MODULE);
+	}
 
-	MOD_INC_USE_COUNT;
 	mcr &= port->phy_node ? ~DTR_PORT1 : ~DTR_PORT0; /* set DTR ON */
 	outb(mcr, io + N2_MCR);
-  
+
 	outb(inb(io + N2_PCR) | PCR_ENWIN, io + N2_PCR); /* open window */
 	outb(inb(io + N2_PSR) | PSR_DMAEN, io + N2_PSR); /* enable dma */
 	sca_open(hdlc);
@@ -237,7 +250,7 @@
 	mcr |= port->phy_node ? DTR_PORT1 : DTR_PORT0; /* set DTR OFF */
 	outb(mcr, io + N2_MCR);
 	hdlc_close(hdlc);
-	MOD_DEC_USE_COUNT;
+	module_put(THIS_MODULE);
 	return 0;
 }
 
@@ -297,62 +310,6 @@
 
 
 
-static u8 n2_count_page(card_t *card)
-{
-	u8 page;
-	int i, bcount = USE_WINDOWSIZE, wcount = USE_WINDOWSIZE/2;
-	u16 *dp = (u16*)card->winbase;
-	u8 *bp = (u8*)card->winbase;
-	u8 psr = inb(card->io + N2_PSR) & PSR_WINBITS;
-
-
-	for (page = 0; page < 16; page++) {
-		outb(psr | page, card->io + N2_PSR); /* select a page */
-		writeb(page, dp);
-		if (readb(dp) != page)
-			break;	/* If can't read back, no good memory */
-
-		outb(psr, card->io + N2_PSR); /* goto page 0 */
-		if (readb(dp))
-			break;	/* If page 0 changed, then wrapped around */
-
-		outb(psr | page, card->io + N2_PSR); /* select page again */
-
-		/*  first do byte tests */
-		for (i = 0; i < bcount; i++)
-			writeb(i, bp + i);
-		for (i = 0; i < bcount; i++)
-			if (readb(bp + i) != (i & 0xff))
-				return 0;
-
-		for (i = 0; i < bcount; i++)
-			writeb(~i, bp + i);
-		for (i = 0; i < bcount; i++)
-			if (readb(bp + i) != (~i & 0xff))
-				return 0;
-
-		/* next do 16-bit tests */
-		for (i = 0; i < wcount; i++)
-			writew(0x55AA, dp + i);
-		for (i = 0; i < wcount; i++)
-			if (readw(dp + i) != 0x55AA)
-				return 0;
-
-		for (i = 0; i < wcount; i++)
-			writew(0xAA55, dp + i);
-		for (i = 0; i < wcount; i++)
-			if (readw(dp + i) != 0xAA55)
-				return 0;
-
-		for (i = 0; i < wcount; i++)
-			writew(page, dp + i);
-	}
-
-	return page;
-}
-
-
-
 static void n2_destroy_card(card_t *card)
 {
 	int cnt;
@@ -376,11 +333,12 @@
 
 
 
-static int n2_run(unsigned long io, unsigned long irq, unsigned long winbase,
-		  long valid0, long valid1)
+static int __init n2_run(unsigned long io, unsigned long irq,
+			 unsigned long winbase, long valid0, long valid1)
 {
 	card_t *card;
 	u8 cnt, pcr;
+	int i;
 
 	if (io < 0x200 || io > 0x3FF || (io % N2_IOPORTS) != 0) {
 		printk(KERN_ERR "n2: invalid I/O port value\n");
@@ -391,7 +349,7 @@
 		printk(KERN_ERR "n2: invalid IRQ value\n");
 		return -ENODEV;
 	}
-    
+
 	if (winbase < 0xA0000 || winbase > 0xFFFFF || (winbase & 0xFFF) != 0) {
 		printk(KERN_ERR "n2: invalid RAM value\n");
 		return -ENODEV;
@@ -451,25 +409,27 @@
 	pcr = PCR_ENWIN | PCR_VPM | (USE_BUS16BITS ? PCR_BUS16 : 0);
 	outb(pcr, io + N2_PCR);
 
-	cnt = n2_count_page(card);
-	if (!cnt) {
-		printk(KERN_ERR "n2: memory test failed.\n");
-		n2_destroy_card(card);
-		return -EIO;
-	}
+	card->ram_size = sca_detect_ram(card, card->winbase, MAX_RAM_SIZE);
 
-	card->ram_size = cnt * USE_WINDOWSIZE;
+	/* number of TX + RX buffers for one port */
+	i = card->ram_size / ((valid0 + valid1) * (sizeof(pkt_desc) +
+						   HDLC_MAX_MRU));
 
-	/* 4 rings required for 2 ports, 2 rings for one port */
-	card->ring_buffers = card->ram_size /
-		((valid0 + valid1) * 2 * (sizeof(pkt_desc) + HDLC_MAX_MRU));
+	card->tx_ring_buffers = min(i / 2, MAX_TX_BUFFERS);
+	card->rx_ring_buffers = i - card->tx_ring_buffers;
 
-	card->buff_offset = (valid0 + valid1) * 2 * (sizeof(pkt_desc))
-		* card->ring_buffers;
+	card->buff_offset = (valid0 + valid1) * sizeof(pkt_desc) *
+		(card->tx_ring_buffers + card->rx_ring_buffers);
 
 	printk(KERN_DEBUG "n2: RISCom/N2 %u KB RAM, IRQ%u, "
-	       "using %u packets rings\n", card->ram_size / 1024, card->irq,
-	       card->ring_buffers);
+	       "using %u TX + %u RX packets rings\n", card->ram_size / 1024,
+	       card->irq, card->tx_ring_buffers, card->rx_ring_buffers);
+
+	if (card->tx_ring_buffers < 1) {
+		printk(KERN_ERR "n2: RAM test failed\n");
+		n2_destroy_card(card);
+		return -EIO;
+	}
 
 	pcr |= PCR_RUNSCA;		/* run SCA */
 	outb(pcr, io + N2_PCR);
@@ -531,7 +491,7 @@
 		return -ENOSYS;	/* no parameters specified, abort */
 	}
 
-	printk(KERN_INFO "%s (SCA-%s)\n", version, sca_version);
+	printk(KERN_INFO "%s\n", version);
 
 	do {
 		unsigned long io, irq, ram;
@@ -558,7 +518,7 @@
 				break;
 			hw++;
 		}
-      
+
 		if (!valid[0] && !valid[1])
 			break;	/* at least one port must be used */
 
@@ -566,7 +526,7 @@
 			n2_run(io, irq, ram, valid[0], valid[1]);
 
 		if (*hw == '\x0')
-			return 0;
+			return first_card ? 0 : -ENOSYS;
 	}while(*hw++ == ':');
 
 	printk(KERN_ERR "n2: invalid hardware parameters\n");
@@ -574,17 +534,6 @@
 }
 
 
-#ifndef MODULE
-static int __init n2_setup(char *str)
-{
-	hw = str;
-	return 1;
-}
-
-__setup("n2=", n2_setup);
-#endif
-
-
 static void __exit n2_cleanup(void)
 {
 	card_t *card = first_card;
@@ -602,5 +551,5 @@
 
 MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
 MODULE_DESCRIPTION("RISCom/N2 serial port driver");
-MODULE_LICENSE("GPL");
-MODULE_PARM(hw, "s");		/* hw=io,irq,ram,ports:io,irq,... */
+MODULE_LICENSE("GPL v2");
+module_param(hw, charp, 0444);	/* hw=io,irq,ram,ports:io,irq,... */
diff -urN linux-2.5.66-bk13/drivers/s390/block/dasd.c linux-2.5.67/drivers/s390/block/dasd.c
--- linux-2.5.66-bk13/drivers/s390/block/dasd.c	2003-03-24 14:00:22.000000000 -0800
+++ linux-2.5.67/drivers/s390/block/dasd.c	2003-04-07 10:55:17.000000000 -0700
@@ -1217,7 +1217,7 @@
 			nr_queued++;
 	}
 	while (!blk_queue_plugged(queue) &&
-	       !blk_queue_empty(queue) &&
+	       elv_next_request(queue) &&
 		nr_queued < DASD_CHANQ_MAX_SIZE) {
 		req = elv_next_request(queue);
 		if (device->ro_flag && rq_data_dir(req) == WRITE) {
diff -urN linux-2.5.66-bk13/drivers/s390/char/tape_block.c linux-2.5.67/drivers/s390/char/tape_block.c
--- linux-2.5.66-bk13/drivers/s390/char/tape_block.c	2003-03-24 14:01:14.000000000 -0800
+++ linux-2.5.67/drivers/s390/char/tape_block.c	2003-04-07 10:55:17.000000000 -0700
@@ -69,7 +69,7 @@
 		device->blk_data.block_position = -1;
 	device->discipline->free_bread(ccw_req);
 	if (!list_empty(&device->req_queue) ||
-	    !blk_queue_empty(&device->blk_data.request_queue))
+	    elv_next_request(&device->blk_data.request_queue))
 		tasklet_schedule(&device->blk_data.tasklet);
 }
 
@@ -95,7 +95,7 @@
 	list_for_each(l, &device->req_queue)
 		nr_queued++;
 	while (!blk_queue_plugged(queue) &&
-	       !blk_queue_empty(queue) &&
+	       elv_next_request(queue) &&
 	       nr_queued < TAPEBLOCK_MIN_REQUEUE) {
 		req = elv_next_request(queue);
 		if (rq_data_dir(req) == WRITE) {
@@ -162,7 +162,7 @@
 	struct tape_device *device;
 
 	device = (struct tape_device *) queue->queuedata;
-	while (!blk_queue_empty(queue)) {
+	while (elv_next_request(queue)) {
 		INIT_LIST_HEAD(&new_req);
 		spin_lock(get_ccwdev_lock(device->cdev));
 		__tape_process_blk_queue(device, &new_req);
@@ -187,7 +187,7 @@
 	struct tape_device *device;
 
 	device = (struct tape_device *) data;
-	while (!blk_queue_empty(&device->blk_data.request_queue)) {
+	while (elv_next_request(&device->blk_data.request_queue)) {
 		INIT_LIST_HEAD(&new_req);
 		spin_lock_irq(get_ccwdev_lock(device->cdev));
 		__tape_process_blk_queue(device, &new_req);
diff -urN linux-2.5.66-bk13/drivers/sbus/char/jsflash.c linux-2.5.67/drivers/sbus/char/jsflash.c
--- linux-2.5.66-bk13/drivers/sbus/char/jsflash.c	2003-03-24 14:00:22.000000000 -0800
+++ linux-2.5.67/drivers/sbus/char/jsflash.c	2003-04-07 10:55:17.000000000 -0700
@@ -190,8 +190,9 @@
 
 static void jsfd_do_request(request_queue_t *q)
 {
-	while (!blk_queue_empty(q)) {
-		struct request *req = elv_next_request(q);
+	struct request *req;
+	
+	while ((req = elv_next_request(q)) != NULL) {
 		struct jsfd_part *jdp = req->rq_disk->private_data;
 		unsigned long offset = req->sector << 9;
 		size_t len = req->current_nr_sectors << 9;
diff -urN linux-2.5.66-bk13/drivers/scsi/scsi_lib.c linux-2.5.67/drivers/scsi/scsi_lib.c
--- linux-2.5.66-bk13/drivers/scsi/scsi_lib.c	2003-04-07 10:55:12.000000000 -0700
+++ linux-2.5.67/drivers/scsi/scsi_lib.c	2003-04-07 10:55:17.000000000 -0700
@@ -398,14 +398,14 @@
 	 * with special case code, then spin off separate versions and
 	 * use function pointers to pick the right one.
 	 */
-	if (sdev->single_lun && blk_queue_empty(q) && sdev->device_busy ==0 &&
-			!shost->host_blocked && !shost->host_self_blocked &&
-			!((shost->can_queue > 0) && (shost->host_busy >=
-				       		     shost->can_queue))) {
+	if (sdev->single_lun && sdev->device_busy == 0 &&
+	    !shost->host_blocked && !shost->host_self_blocked &&
+	    !((shost->can_queue > 0) && (shost->host_busy >= shost->can_queue))
+	    && elv_queue_empty(q)) {
 		list_for_each_entry(sdev2, &sdev->same_target_siblings,
 			       same_target_siblings) {
 			if (!sdev2->device_blocked &&
-			    !blk_queue_empty(sdev2->request_queue)) {
+			    !elv_queue_empty(sdev2->request_queue)) {
 				__blk_run_queue(sdev2->request_queue);
 				break;
 			}
diff -urN linux-2.5.66-bk13/include/linux/blkdev.h linux-2.5.67/include/linux/blkdev.h
--- linux-2.5.66-bk13/include/linux/blkdev.h	2003-03-24 14:00:09.000000000 -0800
+++ linux-2.5.67/include/linux/blkdev.h	2003-04-07 10:55:17.000000000 -0700
@@ -260,7 +260,6 @@
 
 #define blk_queue_plugged(q)	!list_empty(&(q)->plug_list)
 #define blk_queue_tagged(q)	test_bit(QUEUE_FLAG_QUEUED, &(q)->queue_flags)
-#define blk_queue_empty(q)	elv_queue_empty(q)
 #define blk_fs_request(rq)	((rq)->flags & REQ_CMD)
 #define blk_pc_request(rq)	((rq)->flags & REQ_BLOCK_PC)
 #define list_entry_rq(ptr)	list_entry((ptr), struct request, queuelist)
diff -urN linux-2.5.66-bk13/include/linux/hdlc/ioctl.h linux-2.5.67/include/linux/hdlc/ioctl.h
--- linux-2.5.66-bk13/include/linux/hdlc/ioctl.h	2003-03-24 13:59:46.000000000 -0800
+++ linux-2.5.67/include/linux/hdlc/ioctl.h	2003-04-07 10:55:17.000000000 -0700
@@ -34,22 +34,15 @@
 } fr_proto_pvc;          /* for creating/deleting FR PVCs */
 
 typedef struct {
+	unsigned int dlci;
+	char master[IFNAMSIZ];	/* Name of master FRAD device */
+}fr_proto_pvc_info;		/* for returning PVC information only */
+
+typedef struct {
     unsigned int interval;
     unsigned int timeout;
 } cisco_proto;
 
 /* PPP doesn't need any info now - supply length = 0 to ioctl */
 
-union hdlc_settings {
-	raw_hdlc_proto		raw_hdlc;
-	cisco_proto		cisco;
-	fr_proto		fr;
-	fr_proto_pvc		fr_pvc;
-};
-
-union line_settings {
-	sync_serial_settings	sync;
-	te1_settings		te1;
-};
-
 #endif /* __HDLC_IOCTL_H__ */
diff -urN linux-2.5.66-bk13/include/linux/hdlc.h linux-2.5.67/include/linux/hdlc.h
--- linux-2.5.66-bk13/include/linux/hdlc.h	2003-03-24 14:01:50.000000000 -0800
+++ linux-2.5.67/include/linux/hdlc.h	2003-04-07 10:55:17.000000000 -0700
@@ -1,12 +1,11 @@
 /*
  * Generic HDLC support routines for Linux
  *
- * Copyright (C) 1999-2002 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999-2003 Krzysztof Halasa <khc@pm.waw.pl>
  *
  * 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.
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
  */
 
 #ifndef __HDLC_H
@@ -52,7 +51,7 @@
 #include <linux/hdlc/ioctl.h>
 
 #define HDLC_MAX_MTU 1500	/* Ethernet 1500 bytes */
-#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10) /* max 10 bytes for FR */
+#define HDLC_MAX_MRU (HDLC_MAX_MTU + 10 + 14 + 4) /* for ETH+VLAN over FR */
 
 #define MAXLEN_LMISTAT  20	/* max size of status enquiry frame */
 
@@ -145,17 +144,20 @@
 
 
 typedef struct pvc_device_struct {
-	struct net_device netdev; /* PVC net device - must be first */
-	struct net_device_stats stats;
 	struct hdlc_device_struct *master;
-	struct pvc_device_struct *next;
+	struct net_device *main;
+	struct net_device *ether; /* bridged Ethernet interface */
+	struct pvc_device_struct *next;	/* Sorted in ascending DLCI order */
+	int dlci;
+	int open_count;
 
 	struct {
-		int active;
-		int new;
-		int deleted;
-		int fecn;
-		int becn;
+		unsigned int new: 1;
+		unsigned int active: 1;
+		unsigned int exist: 1;
+		unsigned int deleted: 1;
+		unsigned int fecn: 1;
+		unsigned int becn: 1;
 	}state;
 }pvc_device;
 
@@ -180,18 +182,20 @@
 	void (*stop)(struct hdlc_device_struct *hdlc);
 	void (*proto_detach)(struct hdlc_device_struct *hdlc);
 	void (*netif_rx)(struct sk_buff *skb);
+	unsigned short (*type_trans)(struct sk_buff *skb,
+				     struct net_device *dev);
 	int proto;		/* IF_PROTO_HDLC/CISCO/FR/etc. */
 
 	union {
 		struct {
 			fr_proto settings;
 			pvc_device *first_pvc;
-			int pvc_count;
+			int dce_pvc_count;
 
 			struct timer_list timer;
 			int last_poll;
 			int reliable;
-			int changed;
+			int dce_changed;
 			int request;
 			int fullrep_sent;
 			u32 last_errors; /* last errors bit list */
@@ -226,6 +230,7 @@
 
 
 int hdlc_raw_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
+int hdlc_raw_eth_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
 int hdlc_cisco_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
 int hdlc_ppp_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
 int hdlc_fr_ioctl(hdlc_device *hdlc, struct ifreq *ifr);
@@ -254,15 +259,9 @@
 }
 
 
-static __inline__ struct net_device* pvc_to_dev(pvc_device *pvc)
-{
-	return &pvc->netdev;
-}
-
-
 static __inline__ pvc_device* dev_to_pvc(struct net_device *dev)
 {
-	return (pvc_device*)dev;
+	return (pvc_device*)dev->priv;
 }
 
 
@@ -272,19 +271,6 @@
 }
 
 
-static __inline__ const char *pvc_to_name(pvc_device *pvc)
-{
-	return pvc_to_dev(pvc)->name;
-}
-
-
-static __inline__ u16 netdev_dlci(struct net_device *dev)
-{
-	return ntohs(*(u16*)dev->dev_addr);
-}
-
-
-
 static __inline__ u16 q922_to_dlci(u8 *hdr)
 {
 	return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
@@ -345,5 +331,15 @@
 }
 
 
+static __inline__ unsigned short hdlc_type_trans(struct sk_buff *skb,
+						 struct net_device *dev)
+{
+	hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+	if (hdlc->type_trans)
+		return hdlc->type_trans(skb, dev);
+	else
+		return __constant_htons(ETH_P_HDLC);
+}
+
 #endif /* __KERNEL */
 #endif /* __HDLC_H */
diff -urN linux-2.5.66-bk13/include/linux/if.h linux-2.5.67/include/linux/if.h
--- linux-2.5.66-bk13/include/linux/if.h	2003-03-24 14:00:21.000000000 -0800
+++ linux-2.5.67/include/linux/if.h	2003-04-07 10:55:17.000000000 -0700
@@ -21,6 +21,8 @@
 
 #include <linux/types.h>		/* for "__kernel_caddr_t" et al	*/
 #include <linux/socket.h>		/* for "struct sockaddr" et al	*/
+
+#define	IFNAMSIZ	16
 #include <linux/hdlc/ioctl.h>
 
 /* Standard interface flags (netdevice->flags). */
@@ -69,7 +71,11 @@
 #define IF_PROTO_FR_ADD_PVC 0x2004	/*    Create FR PVC		*/
 #define IF_PROTO_FR_DEL_PVC 0x2005	/*    Delete FR PVC		*/
 #define IF_PROTO_X25	0x2006		/* X.25				*/
-
+#define IF_PROTO_HDLC_ETH 0x2007	/* raw HDLC, Ethernet emulation	*/
+#define IF_PROTO_FR_ADD_ETH_PVC 0x2008	/*  Create FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_DEL_ETH_PVC 0x2009	/*  Delete FR Ethernet-bridged PVC */
+#define IF_PROTO_FR_PVC	0x200A		/* for reading PVC status	*/
+#define IF_PROTO_FR_ETH_PVC 0x200B
 
 
 /*
@@ -103,6 +109,7 @@
 		cisco_proto		*cisco;
 		fr_proto		*fr;
 		fr_proto_pvc		*fr_pvc;
+		fr_proto_pvc_info	*fr_pvc_info;
 
 		/* interface settings */
 		sync_serial_settings	*sync;
@@ -120,7 +127,6 @@
 struct ifreq 
 {
 #define IFHWADDRLEN	6
-#define	IFNAMSIZ	16
 	union
 	{
 		char	ifrn_name[IFNAMSIZ];		/* if name, e.g. "en0" */
