From 74beaae07446016bfdf7d1901f622b3e773a716b Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Fri, 23 Nov 2007 15:09:39 -0500 Subject: [PATCH] Import 1.1.47 --- Makefile | 2 +- drivers/block/blk.h | 4 - drivers/block/floppy.c | 2 +- drivers/block/hd.c | 1 + drivers/char/console.c | 4 +- drivers/char/kbd_kern.h | 8 +- drivers/char/keyboard.c | 19 +- drivers/char/n_tty.c | 4 +- drivers/char/tty_io.c | 8 +- drivers/scsi/NCR5380.c | 19 +- drivers/scsi/constants.h | 1 + drivers/scsi/pas16.c | 56 ++- drivers/scsi/sr.c | 15 +- fs/ext2/balloc.c | 5 +- include/asm-i386/bitops.h | 12 + include/linux/ncp.h | 106 +++++ include/linux/socket.h | 1 + net/inet/datagram.c | 3 +- net/inet/ipx.c | 954 +++++++++++++++++++++++++++++++------- net/inet/ncp.h | 26 ++ net/inet/sock.h | 2 + net/socket.c | 3 +- 22 files changed, 1046 insertions(+), 209 deletions(-) create mode 100644 include/linux/ncp.h create mode 100644 net/inet/ncp.h diff --git a/Makefile b/Makefile index 06004edd4ce5..c255661ffd0d 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ VERSION = 1 PATCHLEVEL = 1 -SUBLEVEL = 46 +SUBLEVEL = 47 ARCH = i386 diff --git a/drivers/block/blk.h b/drivers/block/blk.h index c4bf1c34a343..35bff8c4e8e4 100644 --- a/drivers/block/blk.h +++ b/drivers/block/blk.h @@ -223,10 +223,6 @@ static void floppy_off(unsigned int nr); #define DEVICE_ON(device) #define DEVICE_OFF(device) -#else - -#error "unknown blk device" - #endif #if (MAJOR_NR != SCSI_TAPE_MAJOR) diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 8cf568b560c1..6cee5470aee9 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -231,7 +231,7 @@ static struct { {{3, 250, 16, 16, 3000, 100, 300, 0, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, 0, { 4,22,21,30, 3, 0, 0, 0}, 150, 4 }, "720k" }, /*3 1/2 DD*/ -{{4, 500, 16, 16, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, +{{4, 500, 16, 16, 4000, 40, 300, 10, 2, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0, 0, { 7, 4,25,22,31,21,29,11}, 150, 7 }, "1.44M" }, /*3 1/2 HD*/ {{5, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0, diff --git a/drivers/block/hd.c b/drivers/block/hd.c index 6dddcfaf36e7..c13d4e697c26 100644 --- a/drivers/block/hd.c +++ b/drivers/block/hd.c @@ -866,6 +866,7 @@ static int hd_ioctl(struct inode * inode, struct file * file, if (err) return err; memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid)); + return 0; RO_IOCTLS(inode->i_rdev,arg); default: diff --git a/drivers/char/console.c b/drivers/char/console.c index 213daad3c842..2657e6e8a57c 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -1457,13 +1457,13 @@ void poke_blanked_console(void) } } -void * memsetw(void * s,unsigned short c,int count) +void * memsetw(void * s, unsigned short c, unsigned int count) { __asm__("cld\n\t" "rep\n\t" "stosw" : /* no output */ - :"a" (c),"D" (s),"c" (count) + :"a" (c),"D" (s),"c" (count/2) :"cx","di"); return s; } diff --git a/drivers/char/kbd_kern.h b/drivers/char/kbd_kern.h index 7253c8cbea3b..4f852725c602 100644 --- a/drivers/char/kbd_kern.h +++ b/drivers/char/kbd_kern.h @@ -16,13 +16,17 @@ * Note: lockstate is used as index in the array key_map. */ struct kbd_struct { - unsigned char ledstate; /* 3 bits */ + unsigned char ledstate; /* 3 bits */ unsigned char default_ledstate; - unsigned char lockstate; /* 3 bits */ #define VC_SCROLLOCK 0 /* scroll-lock mode */ #define VC_NUMLOCK 1 /* numeric lock mode */ #define VC_CAPSLOCK 2 /* capslock mode */ + unsigned char lockstate; /* 4 bits - must be in 0..15 */ +#define VC_SHIFTLOCK KG_SHIFT /* shift lock mode */ +#define VC_ALTGRLOCK KG_ALTGR /* altgr lock mode */ +#define VC_CTRLLOCK KG_CTRL /* control lock mode */ +#define VC_ALTLOCK KG_ALT /* alt lock mode */ unsigned char modeflags; #define VC_APPLIC 0 /* application key mode */ diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index 739507d1f07a..698ed92cd9f8 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -344,15 +344,15 @@ static void keyboard_interrupt(int int_pt_regs) u_char type; /* the XOR below used to be an OR */ - int shift_final = shift_state ^ vc_kbd_lock(kbd,VC_CAPSLOCK); + int shift_final = shift_state ^ kbd->lockstate; key_code = key_map[shift_final][scancode]; type = KTYP(key_code); if (type == KT_LETTER) { type = KT_LATIN; - if (vc_kbd_lock(kbd,VC_CAPSLOCK)) - key_code = key_map[shift_final][scancode]; + if (vc_kbd_led(kbd,VC_CAPSLOCK)) + key_code = key_map[shift_final ^ (1<driver.flush_chars) - tty->driver.flush_chars(tty); } + if (tty->driver.flush_chars) + tty->driver.flush_chars(tty); } else { c = tty->driver.write(tty, 1, b, nr); b += c; diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index fef159189b47..57cfa0c5407d 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c @@ -1535,8 +1535,10 @@ int tty_register_driver(struct tty_driver *driver) return 0; error = register_chrdev(driver->major, driver->name, &tty_fops); - if (error) + if (error < 0) return error; + else if(driver->major == 0) + driver->major = error; if (!driver->put_char) driver->put_char = tty_default_put_char; @@ -1545,7 +1547,7 @@ int tty_register_driver(struct tty_driver *driver) driver->next = tty_drivers; tty_drivers->prev = driver; tty_drivers = driver; - return 0; + return error; } /* @@ -1589,7 +1591,7 @@ long tty_init(long kmem_start) panic("size of tty structure > PAGE_SIZE!"); if (register_chrdev(TTY_MAJOR,"tty",&tty_fops)) panic("unable to get major %d for tty device", TTY_MAJOR); - if (register_chrdev(TTYAUX_MAJOR,"tty",&tty_fops)) + if (register_chrdev(TTYAUX_MAJOR,"cua",&tty_fops)) panic("unable to get major %d for tty device", TTYAUX_MAJOR); kmem_start = kbd_init(kmem_start); diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c index a0b8b40aca1a..925a61630719 100644 --- a/drivers/scsi/NCR5380.c +++ b/drivers/scsi/NCR5380.c @@ -193,6 +193,11 @@ * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential * transceivers. * + * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512 + * bytes at a time. Since interrupts are disabled by default during + * these transfers, we might need this to give reasonable interrupt + * service time if the transfer size gets too large. + * * LINKED - if defined, linked commands are supported. * * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases. @@ -1980,10 +1985,16 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) { if (!cmd->device->borken && (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) { #else - if (!cmd->device->borken && - (transfersize = cmd->transfersize) && - cmd->SCp.this_residual && !(cmd->SCp.this_residual % - transfersize)) { + transfersize = cmd->transfersize; + +#ifdef LIMIT_TRANSFERSIZE /* If we have problems with interrupt service */ + if( transfersize > 512 ) + transfersize = 512; +#endif /* LIMIT_TRANSFERSIZE */ + + if (!cmd->device->borken && transfersize && + cmd->SCp.this_residual && !(cmd->SCp.this_residual % + transfersize)) { #endif len = transfersize; if (NCR5380_transfer_dma(instance, &phase, diff --git a/drivers/scsi/constants.h b/drivers/scsi/constants.h index d3db95b8c521..f40f300ac256 100644 --- a/drivers/scsi/constants.h +++ b/drivers/scsi/constants.h @@ -4,4 +4,5 @@ extern void print_command(unsigned char *); extern int print_msg(unsigned char *); extern void print_sense(char *, Scsi_Cmnd *); extern void print_status(int); +extern void print_Scsi_Cmnd (Scsi_Cmnd *); #endif /* def _CONSTANTS_H */ diff --git a/drivers/scsi/pas16.c b/drivers/scsi/pas16.c index e8fdb651ec89..5dad9529c84f 100644 --- a/drivers/scsi/pas16.c +++ b/drivers/scsi/pas16.c @@ -1,5 +1,7 @@ #define AUTOSENSE #define PSEUDO_DMA +#define FOO +#define UNSAFE /* Not unsafe for PAS16 -- use it */ /* * This driver adapted from Drew Eckhardt's Trantor T128 driver @@ -40,6 +42,11 @@ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically * for commands that return with a CHECK CONDITION status. * + * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512 + * bytes at a time. Since interrupts are disabled by default during + * these transfers, we might need this to give reasonable interrupt + * service time if the transfer size gets too large. + * * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance * increase compared to polled I/O. * @@ -47,11 +54,13 @@ * * SCSI2 - enable support for SCSI-II tagged queueing. Untested. * - * - * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You - * only really want to use this if you're having a problem with - * dropped characters during high speed communications, and even - * then, you're going to be better off twiddling with transfersize. + * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This + * parameter comes from the NCR5380 code. It is NOT unsafe with + * the PAS16 and you should use it. If you don't you will have + * a problem with dropped characters during high speed + * communications during SCSI transfers. If you really don't + * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or + * twiddle with the transfer size in the high level code. * * USLEEP - enable support for devices that don't disconnect. Untested. * @@ -242,7 +251,7 @@ void init_board( unsigned short io_port, int irq, int force_irq ) int pas16_hw_detect( unsigned short board_num ) { unsigned char board_rev, tmp; - unsigned short port = bases[ board_num ].io_port; + unsigned short io_port = bases[ board_num ].io_port; /* See if we can find a PAS16 board at the address associated * with this logical board number. @@ -251,26 +260,39 @@ int pas16_hw_detect( unsigned short board_num ) /* First, attempt to take a newer model board out of reset and * give it a base address. This shouldn't affect older boards. */ - enable_board( board_num, port ); + enable_board( board_num, io_port ); /* Now see if it looks like a PAS16 board */ - board_rev = inb( port + PCB_CONFIG ); + board_rev = inb( io_port + PCB_CONFIG ); if( board_rev == 0xff ) return 0; tmp = board_rev ^ 0xe0; - outb( tmp, port + PCB_CONFIG ); - tmp = inb( port + PCB_CONFIG ); - outb( board_rev, port + PCB_CONFIG ); + outb( tmp, io_port + PCB_CONFIG ); + tmp = inb( io_port + PCB_CONFIG ); + outb( board_rev, io_port + PCB_CONFIG ); if( board_rev != tmp ) /* Not a PAS-16 */ return 0; - if( ( inb( port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 ) + if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 ) return 0; /* return if no SCSI interface found */ + /* Mediavision has some new model boards that return ID bits + * that indicate a SCSI interface, but they're not (LMS). We'll + * put in an additional test to try and weed them out. + */ + + outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */ + NCR5380_write( MODE_REG, 0x20 ); /* Is it really SCSI? */ + if( NCR5380_read( MODE_REG ) != 0x20 ) /* Write to a reg. */ + return 0; /* and try to read */ + NCR5380_write( MODE_REG, 0x00 ); /* it back. */ + if( NCR5380_read( MODE_REG ) != 0x00 ) + return 0; + return 1; } @@ -426,7 +448,15 @@ int pas16_biosparam(Disk * disk, int dev, int * ip) int size = disk->capacity; ip[0] = 64; ip[1] = 32; - ip[2] = size >> 11; + ip[2] = size >> 11; /* I think I have it as /(32*64) */ + if( ip[2] > 1024 ) { /* yes, >, not >= */ + ip[0]=255; + ip[1]=63; + ip[2]=size/(63*255); + if( ip[2] > 1023 ) /* yes >1023... */ + ip[2] = 1023; + } + return 0; } diff --git a/drivers/scsi/sr.c b/drivers/scsi/sr.c index e1b450b3958b..2b04d78201fb 100644 --- a/drivers/scsi/sr.c +++ b/drivers/scsi/sr.c @@ -627,7 +627,20 @@ are any multiple of 512 bytes long. */ printk("\n"); }; #endif - + +/* Some dumb host adapters can speed transfers by knowing the + * minimum tranfersize in advance. + * + * We shouldn't disconnect in the middle of a sector, but the cdrom + * sector size can be larger than the size of a buffer and the + * transfer may be split to the size of a buffer. So it's safe to + * assume that we can at least transfer the minimum of the buffer + * size (1024) and the sector size between each connect / disconnect. + */ + + SCpnt->transfersize = (scsi_CDs[dev].sector_size > 1024) ? + 1024 : scsi_CDs[dev].sector_size; + SCpnt->this_count = this_count; scsi_do_cmd (SCpnt, (void *) cmd, buffer, realcount * scsi_CDs[dev].sector_size, diff --git a/fs/ext2/balloc.c b/fs/ext2/balloc.c index 60655968a146..0b09426af102 100644 --- a/fs/ext2/balloc.c +++ b/fs/ext2/balloc.c @@ -316,10 +316,7 @@ repeat: else lmap |= 0xffffffff << (31 - (j & 31)); if (lmap != 0xffffffffl) { - __asm__ ("bsfl %1,%0" - : "=r" (k) - : "r" (~lmap)); - k++; + k = ffz(lmap) + 1; if ((j + k) < EXT2_BLOCKS_PER_GROUP(sb)) { j += k; goto got_block; diff --git a/include/asm-i386/bitops.h b/include/asm-i386/bitops.h index fd417b810941..5e2c324cd56f 100644 --- a/include/asm-i386/bitops.h +++ b/include/asm-i386/bitops.h @@ -121,4 +121,16 @@ extern inline int find_next_zero_bit (unsigned long * addr, int size, return (offset + set + res); } +/* + * ffz = Find First Zero in word. Undefined if no zero exists, + * so code should check against ~0UL first.. + */ +extern inline unsigned long ffz(unsigned long word) +{ + __asm__("bsfl %1,%0" + :"=r" (word) + :"r" (~word)); + return word; +} + #endif /* _I386_BITOPS_H */ diff --git a/include/linux/ncp.h b/include/linux/ncp.h new file mode 100644 index 000000000000..bd6daf29dbbc --- /dev/null +++ b/include/linux/ncp.h @@ -0,0 +1,106 @@ +#ifndef _LINUX_NCP_H_ +#define _LINUX_NCP_H_ + +#define NCP_OPEN 0x1111 +#define NCP_CLOSE 0x5555 +#define NCP_REQUEST 0x2222 +#define NCP_REPLY 0x3333 + +struct ncp_request +{ + unsigned short p_type __attribute__ ((packed)); + unsigned char seq __attribute__ ((packed)); + unsigned char c_low __attribute__ ((packed)); + unsigned char task __attribute__ ((packed)); + unsigned char c_high __attribute__ ((packed)); + unsigned char func __attribute__ ((packed)); +}; + +struct ncp_request_sf +{ + unsigned short p_type __attribute__ ((packed)); + unsigned char seq __attribute__ ((packed)); + unsigned char c_low __attribute__ ((packed)); + unsigned char task __attribute__ ((packed)); + unsigned char c_high __attribute__ ((packed)); + unsigned char func __attribute__ ((packed)); + unsigned short s_len __attribute__ ((packed)); + unsigned char s_func __attribute__ ((packed)); +}; + +struct ncp_reply +{ + unsigned short p_type __attribute__ ((packed)); + unsigned char seq __attribute__ ((packed)); + unsigned char c_low __attribute__ ((packed)); + unsigned char task __attribute__ ((packed)); + unsigned char c_high __attribute__ ((packed)); + unsigned char f_stat __attribute__ ((packed)); + unsigned char c_stat __attribute__ ((packed)); +}; + +#define OTYPE_USER 0x0001 +#define OTYPE_GROUP 0x0002 +#define OTYPE_PQUEUE 0x0003 +#define OTYPE_FSERVER 0x0004 +#define OTYPE_JSERVER 0x0005 +#define OTYPE_PSERVER 0x0007 +#define OTYPE_UNKNOWN_1 0x002E +#define OTYPE_ADV_PSERVER 0x0047 +#define OTYPE_AFSERVER 0x0107 +#define OTYPE_UNKNOWN_2 0x0143 +#define OTYPE_UNKNOWN_3 0x01F5 +#define OTYPE_UNKNOWN_4 0x023F + +#define LIMIT_OBJNAME 47 + +struct bind_obj +{ + unsigned long id __attribute__ ((packed)); + unsigned short type __attribute__ ((packed)); + char name[LIMIT_OBJNAME+1] __attribute__ ((packed)); +}; + +struct get_bind_obj +{ + unsigned short type __attribute__ ((packed)); + unsigned char n_len __attribute__ ((packed)); + char name[0] __attribute__ ((packed)); +}; + +struct scan_bind_obj +{ + unsigned long id __attribute__ ((packed)); + unsigned short type __attribute__ ((packed)); + unsigned char n_len __attribute__ ((packed)); + char name[0] __attribute__ ((packed)); +}; + +struct login_req +{ + unsigned char password[8] __attribute__ ((packed)); + unsigned short type __attribute__ ((packed)); + unsigned char n_len __attribute__ ((packed)); + char name[0] __attribute__ ((packed)); +}; + +struct ncp_time +{ + unsigned char year __attribute__ ((packed)); + unsigned char month __attribute__ ((packed)); + unsigned char day __attribute__ ((packed)); + unsigned char hours __attribute__ ((packed)); + unsigned char mins __attribute__ ((packed)); + unsigned char secs __attribute__ ((packed)); + unsigned char c_secs __attribute__ ((packed)); +}; + +struct login_info +{ + unsigned long id __attribute__ ((packed)); + unsigned short un1 __attribute__ ((packed)); + char name[LIMIT_OBJNAME+1] __attribute__ ((packed)); + struct ncp_time time __attribute__ ((packed)); +}; +#endif + diff --git a/include/linux/socket.h b/include/linux/socket.h index 08cb0f901697..71de245419a4 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -20,6 +20,7 @@ struct linger { #define SOCK_RAW 3 /* raw socket */ #define SOCK_RDM 4 /* reliably-delivered message */ #define SOCK_SEQPACKET 5 /* sequential packet socket */ +#define SOCK_NCP 6 /* Novell NCP socket */ #define SOCK_PACKET 10 /* linux specific way of */ /* getting packets at the dev */ /* level. For writing rarp and */ diff --git a/net/inet/datagram.c b/net/inet/datagram.c index 116c16e3e752..998a978136aa 100644 --- a/net/inet/datagram.c +++ b/net/inet/datagram.c @@ -177,7 +177,8 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait) switch(sel_type) { case SEL_IN: - if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE) + if ((sk->type==SOCK_SEQPACKET || sk->type==SOCK_NCP) + && sk->state==TCP_CLOSE) { /* Connection closed: Wake up */ return(1); diff --git a/net/inet/ipx.c b/net/inet/ipx.c index 1578707839f8..ca0d8c0ea27f 100644 --- a/net/inet/ipx.c +++ b/net/inet/ipx.c @@ -56,8 +56,16 @@ #include /* For TIOCOUTQ/INQ */ #include #include "p8022.h" +#include "ncp.h" #ifdef CONFIG_IPX + +static void ipx_delete_timer (ipx_socket *sk); +static int ipx_do_sendto(ipx_socket *sk, ipx_address *ipx, + void *ubuf, int len, int flag, unsigned char type); +static void ipx_add_timer (ipx_socket *sk, int len); +static void ipx_reset_timer (ipx_socket *sk, int len); + /***********************************************************************************************************************\ * * * Handlers for the socket list. * @@ -75,7 +83,9 @@ static ipx_socket *volatile ipx_socket_list=NULL; static void ipx_remove_socket(ipx_socket *sk) { ipx_socket *s; + unsigned long flags; + save_flags(flags); cli(); s=ipx_socket_list; if(s==sk) @@ -89,20 +99,23 @@ static void ipx_remove_socket(ipx_socket *sk) if(s->next==sk) { s->next=sk->next; - sti(); + restore_flags(flags); return; } s=s->next; } - sti(); + restore_flags(flags); } static void ipx_insert_socket(ipx_socket *sk) { + unsigned long flags; + + save_flags(flags); cli(); sk->next=ipx_socket_list; ipx_socket_list=sk; - sti(); + restore_flags(flags); } static ipx_socket *ipx_find_socket(int port) @@ -133,9 +146,10 @@ static void ipx_destroy_socket(ipx_socket *sk) ipx_remove_socket(sk); while((skb=skb_dequeue(&sk->receive_queue))!=NULL) - { kfree_skb(skb,FREE_READ); - } + + while((skb=skb_dequeue(&sk->write_queue))!=NULL) + kfree_skb(skb,FREE_WRITE); kfree_s(sk,sizeof(*sk)); } @@ -151,20 +165,24 @@ int ipx_get_info(char *buffer, char **start, off_t offset, int length) /* Theory.. Keep printing in the same place until we pass offset */ - len += sprintf (buffer,"Type local_address rem_address tx_queue rx_queue st uid\n"); + len += sprintf (buffer," local_address rem_address tx_queue rx_queue st uid\n"); for (s = ipx_socket_list; s != NULL; s = s->next) { - len += sprintf (buffer+len,"%02X ", s->ipx_type); - len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%02X ", htonl(s->ipx_source_addr.net), + len += sprintf (buffer+len,"%02X ", s->ipx_type); + len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%04X ", htonl(s->ipx_source_addr.net), s->ipx_source_addr.node[0], s->ipx_source_addr.node[1], s->ipx_source_addr.node[2], s->ipx_source_addr.node[3], s->ipx_source_addr.node[4], s->ipx_source_addr.node[5], htons(s->ipx_source_addr.sock)); - len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%02X ", htonl(s->ipx_dest_addr.net), + len += sprintf (buffer+len,"%08lX:%02X%02X%02X%02X%02X%02X:%04X ", htonl(s->ipx_dest_addr.net), s->ipx_dest_addr.node[0], s->ipx_dest_addr.node[1], s->ipx_dest_addr.node[2], s->ipx_dest_addr.node[3], s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5], htons(s->ipx_dest_addr.sock)); len += sprintf (buffer+len,"%08lX:%08lX ", s->wmem_alloc, s->rmem_alloc); - len += sprintf (buffer+len,"%02X %d\n", s->state, SOCK_INODE(s->socket)->i_uid); + len += sprintf (buffer+len,"%02X ", s->state); + if (s->socket) + len += sprintf (buffer+len,"%d\n", SOCK_INODE(s->socket)->i_uid); + else + len += sprintf (buffer+len,"%d\n", SOCK_INODE(s->ncp.ncp->socket)->i_uid); /* Are we still dumping unwanted data then discard the record */ pos=begin+len; @@ -566,20 +584,56 @@ static void def_callback2(struct sock *sk, int len) wake_up_interruptible(sk->sleep); } -static int ipx_create(struct socket *sock, int protocol) +static void watch_callback(struct sock *sk, int len) { - ipx_socket *sk; - sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL); - if(sk==NULL) - return(-ENOMEM); - switch(sock->type) + ipx_packet *ipx; + struct sk_buff *skb; + char *data; + + skb=skb_dequeue(&sk->receive_queue); + if(skb==NULL) + return; + + ipx = (ipx_packet *)(skb->h.raw); + data = (char *)(ipx+1); + + if (*(data+1) == '?') + ipx_do_sendto(sk,&(ipx->ipx_source),"\0Y",2,0,sk->ipx_type); + + kfree_skb(skb, FREE_READ); +} + +static void mail_callback(struct sock *sk, int len) +{ + ipx_packet *ipx; + struct sk_buff *skb; + char *data; + + skb=skb_dequeue(&sk->receive_queue); + if(skb==NULL) + return; + + ipx = (ipx_packet *)(skb->h.raw); + data = (char *)(ipx+1); + if (*(data+1) == '!') { - case SOCK_DGRAM: - break; - default: - kfree_s((void *)sk,sizeof(*sk)); - return(-ESOCKTNOSUPPORT); + struct ncp_request_sf req; + + req.func=0x15; + req.s_func=0x01; + req.s_len=htons(1); + + ipx_do_sendto(sk->ncp.ncp, &(sk->ncp.ncp->ipx_dest_addr), + &req, sizeof(req), 0,sk->ncp.ncp->ipx_type); } + + kfree_skb(skb, FREE_READ); + +} + +static void ipx_do_create(struct socket *sock, ipx_socket *sk) +{ + sk->dead=0; sk->next=NULL; sk->broadcast=0; @@ -598,7 +652,6 @@ static int ipx_create(struct socket *sock, int protocol) skb_queue_head_init(&sk->back_log); sk->state=TCP_CLOSE; sk->socket=sock; - sk->type=sock->type; sk->ipx_type=0; /* General user level IPX */ sk->debug=0; @@ -610,14 +663,76 @@ static int ipx_create(struct socket *sock, int protocol) { sock->data=(void *)sk; sk->sleep=sock->wait; + sk->type=sock->type; } + else + sk->type=SOCK_DGRAM; + sk->priority=SOPRI_NORMAL; sk->state_change=def_callback1; sk->data_ready=def_callback2; sk->write_space=def_callback1; sk->error_report=def_callback1; sk->zapped=1; + + return; +} + +static int ncp_create(struct socket *sock, int protocol) +{ + ipx_socket *sk; + ipx_socket *skw; + ipx_socket *skm; + + if ((sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL))==NULL) + return(-ENOMEM); + + if ((skw=(ipx_socket *)kmalloc(sizeof(*skw),GFP_KERNEL))==NULL) + { + kfree_s((void *)sk, sizeof(*sk)); + return(-ENOMEM); + } + + if ((skm=(ipx_socket *)kmalloc(sizeof(*skm),GFP_KERNEL))==NULL) + { + kfree_s((void *)skw, sizeof(*skw)); + kfree_s((void *)sk, sizeof(*sk)); + return(-ENOMEM); + } + + ipx_do_create(sock, sk); + sk->ncp.ncp=NULL; + sk->ncp.watchdog=skw; + sk->ncp.mail=skm; + ipx_do_create(NULL, skw); + skw->ncp.ncp=sk; + skw->data_ready=watch_callback; + ipx_do_create(NULL, skm); + skm->ncp.ncp=sk; + skm->data_ready=mail_callback; + + return(0); +} + +static int ipx_create(struct socket *sock, int protocol) +{ + ipx_socket *sk; + + switch(sock->type) + { + case SOCK_DGRAM: + break; + case SOCK_NCP: + return(ncp_create(sock, protocol)); + default: + return(-ESOCKTNOSUPPORT); + } + if ((sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL))==NULL) + return(-ENOMEM); + + ipx_do_create(sock, sk); + return(0); } @@ -633,47 +748,79 @@ static int ipx_release(struct socket *sock, struct socket *peer) return(0); if(!sk->dead) sk->state_change(sk); - sk->dead=1; + sock->data=NULL; - ipx_destroy_socket(sk); + + if (sk->type == SOCK_NCP) + { + sk->ncp.watchdog->dead=1; + ipx_destroy_socket(sk->ncp.watchdog); + sk->ncp.mail->dead=1; + ipx_destroy_socket(sk->ncp.mail); + + if ((sk->state == TCP_ESTABLISHED) || (sk->state == TCP_SYN_SENT)) + { + struct ncp_request req; + + sk->state=TCP_CLOSE_WAIT; + + ipx_do_sendto(sk, &(sk->ipx_dest_addr), &req, + sizeof(req), 0, sk->ipx_type); + } + else + { + sk->dead=1; + if (sk->state != TCP_CLOSE) + ipx_delete_timer(sk); + ipx_destroy_socket(sk); + } + } + else + { + sk->dead=1; + ipx_destroy_socket(sk); + } return(0); } static unsigned short first_free_socketnum(void) { - static unsigned short socketNum = 0x4000; + static unsigned short socketNum = 0x3fff; - while (ipx_find_socket(htons(socketNum)) != NULL) - if (socketNum > 0x7ffc) socketNum = 0x4000; + while (ipx_find_socket(htons(++socketNum)) != NULL) + if (socketNum > 0x7ffc) socketNum = 0x3fff; - return htons(socketNum++); + return htons(socketNum); } static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) { - ipx_socket *sk; + ipx_socket *sk=(ipx_socket *)sock->data; struct ipx_route *rt; unsigned char *nodestart; struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr; - sk=(ipx_socket *)sock->data; - if(sk->zapped==0) return(-EIO); if(addr_len!=sizeof(struct sockaddr_ipx)) return -EINVAL; + if (addr->sipx_port == 0) { addr->sipx_port = first_free_socketnum(); + if (sk->type == SOCK_NCP) + while ((ipx_find_socket(htons(ntohs(addr->sipx_port)+1))) + || (ipx_find_socket(htons(ntohs(addr->sipx_port)+2)))) + addr->sipx_port = first_free_socketnum(); if (addr->sipx_port == 0) return -EINVAL; } if(ntohs(addr->sipx_port)<0x4000 && !suser()) return(-EPERM); /* protect IPX system stuff like routing/sap */ - + /* Source addresses are easy. It must be our network:node pair for an interface routed to IPX with the ipx routing ioctl() */ @@ -685,6 +832,19 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) return -EADDRINUSE; } + if (sk->type == SOCK_NCP) + { + if ((ipx_find_socket(htons(ntohs(addr->sipx_port)+1))) + || (ipx_find_socket(htons(ntohs(addr->sipx_port)+2)))) + { + if(sk->debug) + printk("IPX: bind failed because port %X in use.\n", + (int)addr->sipx_port); + return -EADDRINUSE; + } + addr->sipx_type=IPX_TYPE_NCP; + } + sk->ipx_source_addr.sock=addr->sipx_port; if (addr->sipx_network == 0L) @@ -705,6 +865,7 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) } sk->ipx_source_addr.net=rt->net; + sk->ipx_type=addr->sipx_type; /* IPX addresses zero pad physical addresses less than 6 */ memset(sk->ipx_source_addr.node,'\0',6); @@ -713,41 +874,102 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) ipx_insert_socket(sk); sk->zapped=0; + + if (sk->type == SOCK_NCP) + { + sk->ncp.watchdog->ipx_source_addr.net=rt->net; + sk->ncp.watchdog->ipx_source_addr.sock=htons(ntohs(addr->sipx_port)+1); + + memset(sk->ncp.watchdog->ipx_source_addr.node,'\0',6); + nodestart = sk->ncp.watchdog->ipx_source_addr.node + (6 - rt->dev->addr_len); + memcpy(nodestart,rt->dev->dev_addr,rt->dev->addr_len); + + ipx_insert_socket(sk->ncp.watchdog); + sk->ncp.watchdog->zapped=0; + + sk->ncp.mail->ipx_source_addr.net=rt->net; + sk->ncp.mail->ipx_source_addr.sock=htons(ntohs(addr->sipx_port)+2); + + memset(sk->ncp.mail->ipx_source_addr.node,'\0',6); + nodestart = sk->ncp.mail->ipx_source_addr.node + (6 - rt->dev->addr_len); + memcpy(nodestart,rt->dev->dev_addr,rt->dev->addr_len); + + ipx_insert_socket(sk->ncp.mail); + sk->ncp.mail->zapped=0; + + sk->mtu=rt->dev->mtu; + } + if(sk->debug) printk("IPX: socket is bound.\n"); return(0); } +static int ncp_connect(struct socket *sock, ipx_socket *sk) +{ + struct ncp_request req; + int err; + + sk->ncp.conn=0xffff; + sk->ncp.seq=0; + + sock->state = SS_CONNECTING; + sk->state = TCP_SYN_SENT; + sk->rto = 0; + + ipx_do_sendto(sk, &(sk->ipx_dest_addr), &req, sizeof(req), 0, sk->ipx_type); + + while(sk->state != TCP_ESTABLISHED) + { + if (sk->err) + { + err=sk->err; + sk->err=0; + return -err; + } + interruptible_sleep_on(sk->sleep); + } + + return(0); +} + static int ipx_connect(struct socket *sock, struct sockaddr *uaddr, int addr_len, int flags) { ipx_socket *sk=(ipx_socket *)sock->data; - struct sockaddr_ipx *addr; - + struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr; + sk->state = TCP_CLOSE; sock->state = SS_UNCONNECTED; - if(addr_len!=sizeof(addr)) + if(addr_len!=sizeof(struct sockaddr_ipx)) return(-EINVAL); - addr=(struct sockaddr_ipx *)uaddr; - if(sk->ipx_source_addr.net==0) + if(sk->ipx_source_addr.sock==0) /* put the autobinding in */ { - struct sockaddr_ipx uaddr; int ret; + struct sockaddr_ipx addr; - uaddr.sipx_port = 0; - uaddr.sipx_network = 0L; - ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx)); + addr.sipx_type = 0; + addr.sipx_port = 0; + addr.sipx_network = 0L; + ret = ipx_bind (sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_ipx)); if (ret != 0) return (ret); } + sk->ipx_dest_addr.net=addr->sipx_network; sk->ipx_dest_addr.sock=addr->sipx_port; memcpy(sk->ipx_dest_addr.node,addr->sipx_node,sizeof(sk->ipx_source_addr.node)); if(ipxrtr_get_dev(sk->ipx_dest_addr.net)==NULL) return -ENETUNREACH; + + if (sk->type == SOCK_NCP) + return(ncp_connect(sock, sk)); + + sk->ipx_type=addr->sipx_type; + sock->state = SS_CONNECTED; sk->state=TCP_ESTABLISHED; return(0); @@ -770,10 +992,9 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, { ipx_address *addr; struct sockaddr_ipx sipx; - ipx_socket *sk; - - sk=(ipx_socket *)sock->data; + ipx_socket *sk=(ipx_socket *)sock->data; + *uaddr_len = sizeof(struct sockaddr_ipx); if(peer) @@ -783,7 +1004,21 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, addr=&sk->ipx_dest_addr; } else + { + if(sk->ipx_source_addr.sock==0) + /* put the autobinding in */ + { + int ret; + + sipx.sipx_type = 0; + sipx.sipx_port = 0; + sipx.sipx_network = 0L; + ret = ipx_bind (sock, (struct sockaddr *)&sipx, sizeof(struct sockaddr_ipx)); + if (ret != 0) return (ret); + } + addr=&sk->ipx_source_addr; + } sipx.sipx_family = AF_IPX; sipx.sipx_port = addr->sock; @@ -793,6 +1028,411 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, return(0); } +static int ipx_build_header(ipx_address *ipx, struct sk_buff *skb, + ipx_socket *sk, int len, unsigned char type) +{ + ipx_packet *ipx_pack; + ipx_route *rt; + struct datalink_proto *dl = NULL; + unsigned char IPXaddr[6]; + int self_addressing = 0; + int broadcast = 0; + + if(sk->debug) + printk("IPX: build_header: Addresses built.\n"); + + if(memcmp(&ipx->node,&ipx_broadcast_node,6)==0) + { + if (!sk->broadcast) + return -ENETUNREACH; + broadcast = 1; + } + + /* Build a packet */ + + if(sk->debug) + printk("IPX: build_header: building packet.\n"); + + /* Find out where this has to go */ + if (ipx->net == 0L) { + rt = ipxrtr_get_default_net(); + if (rt != NULL) + ipx->net = rt->net; + } else + rt=ipxrtr_get_dev(ipx->net); + + if(rt==NULL) + { + return -ENETUNREACH; + } + + dl=rt->datalink; + + skb->mem_addr=skb; + skb->sk=sk; + skb->free=1; + skb->arp=1; + skb->tries=0; + + if(sk->debug) + printk("Building MAC header.\n"); + skb->dev=rt->dev; + + /* Build Data Link header */ + dl->datalink_header(dl, skb, + (rt->flags&IPX_RT_ROUTED)?rt->router_node:ipx->node); + + /* See if we are sending to ourself */ + memset(IPXaddr, '\0', 6); + memcpy(IPXaddr+(6 - skb->dev->addr_len), skb->dev->dev_addr, + skb->dev->addr_len); + + self_addressing = !memcmp(IPXaddr, + (rt->flags&IPX_RT_ROUTED)?rt->router_node + :ipx->node, + 6); + + /* Now the IPX */ + if(sk->debug) + printk("Building IPX Header.\n"); + ipx_pack=(ipx_packet *)skb->h.raw; + ipx_pack->ipx_checksum=0xFFFF; + ipx_pack->ipx_pktsize=htons(len+sizeof(ipx_packet)); + ipx_pack->ipx_tctrl=0; + ipx_pack->ipx_type=type; + + memcpy(&ipx_pack->ipx_source,&sk->ipx_source_addr,sizeof(ipx_pack->ipx_source)); + memcpy(&ipx_pack->ipx_dest,ipx,sizeof(ipx_pack->ipx_dest)); + + if((skb->dev->flags&IFF_LOOPBACK) || self_addressing) + skb->pkt_type=PACKET_HOST; + else + if (broadcast) + skb->pkt_type=PACKET_BROADCAST; + else + skb->pkt_type=PACKET_OTHERHOST; + + return 0; +} + +static int ipx_xmit(struct sk_buff *skb) +{ + struct sk_buff *skb1; + struct device *dev= skb->dev; + ipx_packet *ipx=(ipx_packet *)skb->h.raw; + ipx_route *rt; + struct packet_type pt; + + if (ipx->ipx_dest.net == 0L) + rt = ipxrtr_get_default_net(); + else + rt=ipxrtr_get_dev(ipx->ipx_dest.net); + + if (rt == NULL) + return -ENETUNREACH; + + pt.type=rt->dlink_type; + + skb->tries++; + + switch (skb->pkt_type) + { + case PACKET_HOST: + if (!skb->free) + { + skb1=alloc_skb(skb->len, GFP_ATOMIC); + if (skb1 != NULL) + { + skb1->mem_addr=skb1; + skb1->free=1; + skb1->arp=1; + skb1->len=skb->len; + skb1->sk = NULL; + skb1->h.raw = skb1->data + rt->datalink->header_length + + dev->hard_header_len; + memcpy(skb1->data, skb->data, skb->len); + ipx_rcv(skb1,dev,&pt); + } + } + else + { + + + /* loop back */ + skb->sk->wmem_alloc-=skb->mem_len; + skb->sk = NULL; + ipx_rcv(skb,dev,&pt); + } + break; + + case PACKET_BROADCAST: + skb1=alloc_skb(skb->len, GFP_ATOMIC); + if (skb1 != NULL) + { + skb1->mem_addr=skb1; + skb1->free=1; + skb1->arp=1; + skb1->len=skb->len; + skb1->sk = NULL; + skb1->h.raw = skb1->data + rt->datalink->header_length + + dev->hard_header_len; + memcpy(skb1->data, skb->data, skb->len); + ipx_rcv(skb1,dev,&pt); + } + default: + if (!skb->free) + { + skb1=alloc_skb(skb->len, GFP_ATOMIC); + if (skb1 != NULL) + { + skb1->mem_addr=skb1; + skb1->free=1; + skb1->arp=1; + skb1->len=skb->len; + skb1->sk = NULL; + skb1->h.raw = skb1->data + rt->datalink->header_length + + dev->hard_header_len; + memcpy(skb1->data, skb->data, skb->len); + } + } + else + skb1=skb; + if (skb1 != NULL) + { + if (skb1->sk) + dev_queue_xmit(skb1,dev,skb->sk->priority); + else + dev_queue_xmit(skb1,dev,SOPRI_NORMAL); + } + } + + return(0); + +} + +static int ipx_retransmit(ipx_socket *sk) +{ + struct sk_buff *skb = sk->write_queue.next; + int num=0; + + ipx_packet *ipx; + struct ncp_request *req; + + if (skb == NULL) + return(num); + + if (skb == skb->next) + return(num); + + do + { + ipx=(ipx_packet *)skb->h.raw; + req=(struct ncp_request *)(ipx+1); + + ipx_xmit(skb); + + num++; + skb=skb->next; + } + while (skb->next != sk->write_queue.next); + + return (num); +} + +static void ipx_timer (unsigned long data) +{ + ipx_socket *sk = (ipx_socket *) data; + int num; + + cli(); + if (in_bh) + { + sk->timer.expires = 10; + add_timer(&sk->timer); + sti(); + return; + } + sti(); + + num=ipx_retransmit(sk); + + sk->rto++; + + if (sk->rto >= MAX_TIMEOUT) + { + struct sk_buff *skb; + + while((skb=skb_dequeue(&sk->write_queue))!=NULL) + kfree_skb(skb,FREE_WRITE); + + sk->err=ETIMEDOUT; + sk->state=TCP_CLOSE; + sk->socket->state=SS_UNCONNECTED; + if(!sk->dead) + sk->error_report(sk); + return; + } + + if (num) + ipx_reset_timer(sk, NCP_TIMEOUT); + + return; +} + +static void ipx_delete_timer (ipx_socket *sk) +{ + unsigned long flags; + + save_flags (flags); + cli(); + + del_timer (&sk->timer); + + restore_flags(flags); +} + +static void ipx_add_timer (ipx_socket *sk, int len) +{ + init_timer (&sk->timer); + sk->timer.data = (unsigned long) sk; + sk->timer.function = &ipx_timer; + sk->timer.expires = len; + add_timer(&sk->timer); +} + +static void ipx_reset_timer (ipx_socket *sk, int len) +{ + + ipx_delete_timer (sk); + sk->timer.data = (unsigned long) sk; + sk->timer.function = &ipx_timer; + sk->timer.expires = len; + add_timer(&sk->timer); +} + +static struct sk_buff *find_req(ipx_socket *sk, unsigned char seq) +{ + ipx_packet *ipx; + struct ncp_request *req; + struct sk_buff *skb = sk->write_queue.next; + + if (skb == NULL) + return (NULL); + + if (skb == skb->next) + return (NULL); + + do + { + ipx=(ipx_packet *)skb->h.raw; + req=(struct ncp_request *)(ipx+1); + if (req->seq == seq) + { + skb_unlink(skb); + return (skb); + } + skb=skb->next; + } + while (skb->next != sk->write_queue.next); + + return (NULL); +} + +static int ncp_rcv(ipx_socket *sk, struct sk_buff *skb) +{ + ipx_packet *ipx=(ipx_packet *)skb->h.raw; + struct ncp_reply *rep= (struct ncp_reply *)(ipx+1); + struct ncp_request_sf *req; + struct sk_buff *skb1; + + if (rep->p_type != NCP_REPLY) + { + kfree_skb(skb, FREE_READ); + return (0); + } + + skb1=find_req(sk, rep->seq); + + if (skb1 == NULL) + { + kfree_skb(skb, FREE_READ); + return (0); + } + + if (&sk->write_queue == sk->write_queue.next) + { + sk->rto=0; + ipx_delete_timer(sk); + } + + ipx=(ipx_packet *)skb1->h.raw; + req=(struct ncp_request_sf *)(ipx+1); + + switch (sk->state) + { + case TCP_CLOSE_WAIT: + kfree_skb(skb, FREE_READ); + sk->socket->data = NULL; + sk->state=TCP_CLOSE; + if(!sk->dead) + sk->state_change(sk); + sk->dead=1; + if (&sk->write_queue != sk->write_queue.next) + ipx_delete_timer(sk); + ipx_destroy_socket(sk); + break; + case TCP_SYN_SENT: + + if ((rep->f_stat == 0) && (rep->c_stat == 0)) + { + sk->state=TCP_ESTABLISHED; + sk->socket->state = SS_CONNECTED; + sk->ncp.conn=rep->c_low + (rep->c_high * 0xff); + if(!sk->dead) + sk->state_change(sk); + } + else + { + sk->state=TCP_CLOSE; + sk->socket->state = SS_UNCONNECTED; + sk->err=ECONNREFUSED; + if (&sk->write_queue != sk->write_queue.next) + ipx_delete_timer(sk); + if(!sk->dead) + sk->error_report(sk); + } + kfree_skb(skb, FREE_READ); + break; + default: + if ((req->func==0x15)&&(req->s_func==0x01)) + { + char *data = (char *)(rep+1); + int len=(int)*data; + + if (len != 0) + { + memcpy(data, data+1, len); + *(data+len)='\0'; + printk("\007%s\n",data); + } + kfree_skb(skb, FREE_READ); + } + else + { + sk->rmem_alloc+=skb->mem_len; + skb->sk = sk; + + skb_queue_tail(&sk->receive_queue,skb); + if(!sk->dead) + sk->data_ready(sk,skb->len); + } + } + + kfree_skb(skb1, FREE_WRITE); + + return(0); +} + int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) { /* NULL here for pt means the packet was looped back */ @@ -942,6 +1582,9 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) kfree_skb(skb,FREE_READ); /* Socket is full */ return(0); } + + if (sock->type == SOCK_NCP) + return (ncp_rcv(sock, skb)); sock->rmem_alloc+=skb->mem_len; skb->sk = sock; @@ -952,65 +1595,27 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) return(0); } -static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock, - unsigned flags, struct sockaddr *usip, int addr_len) +static int ipx_do_sendto(ipx_socket *sk, ipx_address *ipx, + void *ubuf, int len, int flag, unsigned char type) { - ipx_socket *sk=(ipx_socket *)sock->data; - struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip; - struct sockaddr_ipx local_sipx; struct sk_buff *skb; struct device *dev; - struct ipx_packet *ipx; + ipx_packet *ipx_pack; int size; ipx_route *rt; struct datalink_proto *dl = NULL; - unsigned char IPXaddr[6]; - int self_addressing = 0; - int broadcast = 0; - if(flags) - return -EINVAL; - - if(usipx) - { - if(sk->ipx_source_addr.net==0) - /* put the autobinding in */ - { - struct sockaddr_ipx uaddr; - int ret; - - uaddr.sipx_port = 0; - uaddr.sipx_network = 0L; - ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx)); - if (ret != 0) return (ret); - } - if(addr_len sipx_family != AF_IPX) - return -EINVAL; - if(htons(usipx->sipx_port)<0x4000 && !suser()) - return -EPERM; - } - else - { - if(sk->state!=TCP_ESTABLISHED) - return -ENOTCONN; - usipx=&local_sipx; - usipx->sipx_family=AF_IPX; - usipx->sipx_port=sk->ipx_dest_addr.sock; - usipx->sipx_network=sk->ipx_dest_addr.net; - memcpy(usipx->sipx_node,sk->ipx_dest_addr.node,sizeof(usipx->sipx_node)); - } - if(sk->debug) printk("IPX: sendto: Addresses built.\n"); - if(memcmp(&usipx->sipx_node,&ipx_broadcast_node,6)==0) + if ((sk->type == SOCK_NCP) && (len < sizeof (struct ncp_request))) + return -EINVAL; + + if(memcmp(&ipx->node,&ipx_broadcast_node,6)==0) { if (!sk->broadcast) return -ENETUNREACH; - broadcast = 1; } /* Build a packet */ @@ -1021,12 +1626,12 @@ static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock, size=sizeof(ipx_packet)+len; /* For mac headers */ /* Find out where this has to go */ - if (usipx->sipx_network == 0L) { + if (ipx->net == 0L) { rt = ipxrtr_get_default_net(); if (rt != NULL) - usipx->sipx_network = rt->net; + ipx->net = rt->net; } else - rt=ipxrtr_get_dev(usipx->sipx_network); + rt=ipxrtr_get_dev(ipx->net); if(rt==NULL) { @@ -1046,86 +1651,117 @@ static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock, return -EAGAIN; } - skb=alloc_skb(size,GFP_KERNEL); + if (flag) + skb=alloc_skb(size,GFP_KERNEL); + else + skb=alloc_skb(size,GFP_ATOMIC); + if(skb==NULL) return -ENOMEM; + + sk->wmem_alloc+=skb->mem_len; - skb->mem_addr=skb; - skb->sk=sk; - skb->free=1; - skb->arp=1; skb->len=size; - sk->wmem_alloc+=skb->mem_len; + ipx_build_header(ipx, skb, sk, len, type); - if(sk->debug) - printk("Building MAC header.\n"); - skb->dev=rt->dev; - - /* Build Data Link header */ - dl->datalink_header(dl, skb, - (rt->flags&IPX_RT_ROUTED)?rt->router_node:usipx->sipx_node); + ipx_pack = (ipx_packet *)(skb->h.raw); - /* See if we are sending to ourself */ - memset(IPXaddr, '\0', 6); - memcpy(IPXaddr+(6 - skb->dev->addr_len), skb->dev->dev_addr, - skb->dev->addr_len); + /* User data follows immediately after the IPX data */ + if (flag) + memcpy_fromfs((char *)(ipx_pack+1),ubuf,len); + else + memcpy((char *)(ipx_pack+1),ubuf,len); - self_addressing = !memcmp(IPXaddr, - (rt->flags&IPX_RT_ROUTED)?rt->router_node - :usipx->sipx_node, - 6); + if (sk->type == SOCK_NCP) + { + struct ncp_request *req=(struct ncp_request *)(ipx_pack+1); + + switch (sk->state) + { + case TCP_SYN_SENT: + req->p_type = NCP_OPEN; + break; + case TCP_CLOSE_WAIT: + req->p_type = NCP_CLOSE; + break; + default: + req->p_type = NCP_REQUEST; + } + req->c_low = (sk->ncp.conn) & 0xff; + req->c_high = (sk->ncp.conn >>8) & 0xff; + req->seq = (sk->ncp.seq)++; + req->task = 1; + + skb->free=0; + + if (&sk->write_queue == sk->write_queue.next) + ipx_add_timer(sk, NCP_TIMEOUT); + else + ipx_reset_timer(sk, NCP_TIMEOUT); + + skb_queue_tail(&sk->write_queue,skb); + } - /* Now the IPX */ - if(sk->debug) - printk("Building IPX Header.\n"); - ipx=(ipx_packet *)skb->h.raw; - ipx->ipx_checksum=0xFFFF; - ipx->ipx_pktsize=htons(len+sizeof(ipx_packet)); - ipx->ipx_tctrl=0; - ipx->ipx_type=usipx->sipx_type; - - memcpy(&ipx->ipx_source,&sk->ipx_source_addr,sizeof(ipx->ipx_source)); - ipx->ipx_dest.net=usipx->sipx_network; - memcpy(ipx->ipx_dest.node,usipx->sipx_node,sizeof(ipx->ipx_dest.node)); - ipx->ipx_dest.sock=usipx->sipx_port; - if(sk->debug) - printk("IPX: Appending user data.\n"); - /* User data follows immediately after the IPX data */ - memcpy_fromfs((char *)(ipx+1),ubuf,len); if(sk->debug) printk("IPX: Transmitting buffer\n"); - if((dev->flags&IFF_LOOPBACK) || self_addressing) { - struct packet_type pt; - - /* loop back */ - pt.type = rt->dlink_type; - sk->wmem_alloc-=skb->mem_len; - skb->sk = NULL; - ipx_rcv(skb,dev,&pt); - } else { - if (broadcast) { - struct packet_type pt; - struct sk_buff *skb2; - /* loop back */ - pt.type = rt->dlink_type; - - skb2=alloc_skb(skb->len, GFP_ATOMIC); - skb2->mem_addr=skb2; - skb2->free=1; - skb2->arp=1; - skb2->len=skb->len; - skb2->sk = NULL; - skb2->h.raw = skb2->data + rt->datalink->header_length - + dev->hard_header_len; - memcpy(skb2->data, skb->data, skb->len); - ipx_rcv(skb2,dev,&pt); + ipx_xmit(skb); + + return len; +} + +static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock, + unsigned flags, struct sockaddr *usip, int addr_len) +{ + ipx_socket *sk=(ipx_socket *)sock->data; + ipx_address ipx; + struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip; + struct sockaddr_ipx local_sipx; + + if(flags) + return -EINVAL; + + if(usipx) + { + if (sk->type == SOCK_NCP) + return -EINVAL; + + if(sk->ipx_source_addr.sock==0) + /* put the autobinding in */ + { + int ret; + + local_sipx.sipx_type = 0; + local_sipx.sipx_port = 0; + local_sipx.sipx_network = 0L; + ret = ipx_bind (sock, (struct sockaddr *)&local_sipx, sizeof(struct sockaddr_ipx)); + if (ret != 0) return (ret); } - dev_queue_xmit(skb,dev,SOPRI_NORMAL); + + if(addr_len sipx_family != AF_IPX) + return -EINVAL; + if(htons(usipx->sipx_port)<0x4000 && !suser()) + return -EPERM; + + ipx.net=usipx->sipx_network; + ipx.sock=usipx->sipx_port; + memcpy(ipx.node,usipx->sipx_node, sizeof(ipx.node)); + return (ipx_do_sendto(sk, &ipx, ubuf, len, 1, + usipx->sipx_type)); + } + else + { + if(sk->state!=TCP_ESTABLISHED) + return -ENOTCONN; + + return (ipx_do_sendto(sk, &(sk->ipx_dest_addr), ubuf, + len, 1, sk->ipx_type)); } - return len; } + static int ipx_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags) { @@ -1142,7 +1778,7 @@ static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, int copied = 0; struct sk_buff *skb; int er; - + if(sk->err) { er= -sk->err; @@ -1170,12 +1806,14 @@ static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, sipx->sipx_type = ipx->ipx_type; } skb_free_datagram(skb); + return(copied); } static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock) { + return ipx_send(sock,ubuf,size,noblock,0); } diff --git a/net/inet/ncp.h b/net/inet/ncp.h new file mode 100644 index 000000000000..b12011c985b2 --- /dev/null +++ b/net/inet/ncp.h @@ -0,0 +1,26 @@ +/* + * + * Kernel support for NCP + * + * Mark Evans 1994 + * + */ + +#ifndef _NCP_H +#define _NCP_H + +#include + +struct ncp_info +{ + unsigned short conn; /* connection number */ + unsigned char seq; /* sequence number */ + ipx_socket *ncp; /* ncp socket */ + ipx_socket *watchdog; /* watchdog socket */ + ipx_socket *mail; /* mail socket */ +}; + +#define NCP_TIMEOUT (3*HZ) +#define MAX_TIMEOUT 15 + +#endif /* _NCP_H */ diff --git a/net/inet/sock.h b/net/inet/sock.h index 331e2cfa0ad3..5352af0bab59 100644 --- a/net/inet/sock.h +++ b/net/inet/sock.h @@ -41,6 +41,7 @@ #endif #ifdef CONFIG_IPX #include "ipx.h" +#include "ncp.h" #endif #define SOCK_ARRAY_SIZE 64 @@ -137,6 +138,7 @@ struct sock { #ifdef CONFIG_IPX ipx_address ipx_source_addr,ipx_dest_addr; unsigned short ipx_type; + struct ncp_info ncp; #endif #ifdef CONFIG_AX25 /* Really we want to add a per protocol private area */ diff --git a/net/socket.c b/net/socket.c index 773fcc0f2737..bfb3249f3034 100644 --- a/net/socket.c +++ b/net/socket.c @@ -592,7 +592,8 @@ static int sock_socket(int family, int type, int protocol) if ((type != SOCK_STREAM && type != SOCK_DGRAM && type != SOCK_SEQPACKET && type != SOCK_RAW && - type != SOCK_PACKET) || protocol < 0) + type != SOCK_PACKET && type != SOCK_NCP) + || protocol < 0) return(-EINVAL); /* -- 2.39.5