Commit 74beaae0 authored by Linus Torvalds's avatar Linus Torvalds

Import 1.1.47

parent ab114917
VERSION = 1 VERSION = 1
PATCHLEVEL = 1 PATCHLEVEL = 1
SUBLEVEL = 46 SUBLEVEL = 47
ARCH = i386 ARCH = i386
......
...@@ -223,10 +223,6 @@ static void floppy_off(unsigned int nr); ...@@ -223,10 +223,6 @@ static void floppy_off(unsigned int nr);
#define DEVICE_ON(device) #define DEVICE_ON(device)
#define DEVICE_OFF(device) #define DEVICE_OFF(device)
#else
#error "unknown blk device"
#endif #endif
#if (MAJOR_NR != SCSI_TAPE_MAJOR) #if (MAJOR_NR != SCSI_TAPE_MAJOR)
......
...@@ -231,7 +231,7 @@ static struct { ...@@ -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, {{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*/ 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*/ 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, {{5, 1000, 15, 8, 3000, 40, 300, 10, 2, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
......
...@@ -866,6 +866,7 @@ static int hd_ioctl(struct inode * inode, struct file * file, ...@@ -866,6 +866,7 @@ static int hd_ioctl(struct inode * inode, struct file * file,
if (err) if (err)
return err; return err;
memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid)); memcpy_tofs((char *)arg, (char *) hd_ident_info[dev], sizeof(struct hd_driveid));
return 0;
RO_IOCTLS(inode->i_rdev,arg); RO_IOCTLS(inode->i_rdev,arg);
default: default:
......
...@@ -1457,13 +1457,13 @@ void poke_blanked_console(void) ...@@ -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" __asm__("cld\n\t"
"rep\n\t" "rep\n\t"
"stosw" "stosw"
: /* no output */ : /* no output */
:"a" (c),"D" (s),"c" (count) :"a" (c),"D" (s),"c" (count/2)
:"cx","di"); :"cx","di");
return s; return s;
} }
......
...@@ -18,11 +18,15 @@ ...@@ -18,11 +18,15 @@
struct kbd_struct { struct kbd_struct {
unsigned char ledstate; /* 3 bits */ unsigned char ledstate; /* 3 bits */
unsigned char default_ledstate; unsigned char default_ledstate;
unsigned char lockstate; /* 3 bits */
#define VC_SCROLLOCK 0 /* scroll-lock mode */ #define VC_SCROLLOCK 0 /* scroll-lock mode */
#define VC_NUMLOCK 1 /* numeric lock mode */ #define VC_NUMLOCK 1 /* numeric lock mode */
#define VC_CAPSLOCK 2 /* capslock 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; unsigned char modeflags;
#define VC_APPLIC 0 /* application key mode */ #define VC_APPLIC 0 /* application key mode */
......
...@@ -344,15 +344,15 @@ static void keyboard_interrupt(int int_pt_regs) ...@@ -344,15 +344,15 @@ static void keyboard_interrupt(int int_pt_regs)
u_char type; u_char type;
/* the XOR below used to be an OR */ /* 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]; key_code = key_map[shift_final][scancode];
type = KTYP(key_code); type = KTYP(key_code);
if (type == KT_LETTER) { if (type == KT_LETTER) {
type = KT_LATIN; type = KT_LATIN;
if (vc_kbd_lock(kbd,VC_CAPSLOCK)) if (vc_kbd_led(kbd,VC_CAPSLOCK))
key_code = key_map[shift_final][scancode]; key_code = key_map[shift_final ^ (1<<KG_SHIFT)][scancode];
} }
(*key_handler[type])(key_code & 0xff, up_flag); (*key_handler[type])(key_code & 0xff, up_flag);
} }
...@@ -404,7 +404,6 @@ static void caps_toggle(void) ...@@ -404,7 +404,6 @@ static void caps_toggle(void)
if (rep) if (rep)
return; return;
chg_vc_kbd_led(kbd,VC_CAPSLOCK); chg_vc_kbd_led(kbd,VC_CAPSLOCK);
chg_vc_kbd_lock(kbd,VC_CAPSLOCK);
} }
static void caps_on(void) static void caps_on(void)
...@@ -412,7 +411,6 @@ static void caps_on(void) ...@@ -412,7 +411,6 @@ static void caps_on(void)
if (rep) if (rep)
return; return;
set_vc_kbd_led(kbd,VC_CAPSLOCK); set_vc_kbd_led(kbd,VC_CAPSLOCK);
set_vc_kbd_lock(kbd,VC_CAPSLOCK);
} }
static void show_ptregs(void) static void show_ptregs(void)
...@@ -463,10 +461,8 @@ static void num(void) ...@@ -463,10 +461,8 @@ static void num(void)
applkey('P', 1); applkey('P', 1);
return; return;
} }
if (!rep) { /* no autorepeat for numlock, ChN */ if (!rep) /* no autorepeat for numlock, ChN */
chg_vc_kbd_led(kbd,VC_NUMLOCK); chg_vc_kbd_led(kbd,VC_NUMLOCK);
chg_vc_kbd_lock(kbd,VC_NUMLOCK);
}
} }
static void lastcons(void) static void lastcons(void)
...@@ -647,7 +643,8 @@ static void do_pad(unsigned char value, char up_flag) ...@@ -647,7 +643,8 @@ static void do_pad(unsigned char value, char up_flag)
applkey(app_map[value], 1); applkey(app_map[value], 1);
return; return;
} }
if (!vc_kbd_lock(kbd,VC_NUMLOCK))
if (!vc_kbd_led(kbd,VC_NUMLOCK))
switch (value) { switch (value) {
case KVAL(K_PCOMMA): case KVAL(K_PCOMMA):
case KVAL(K_PDOT): case KVAL(K_PDOT):
...@@ -709,10 +706,8 @@ static void do_shift(unsigned char value, char up_flag) ...@@ -709,10 +706,8 @@ static void do_shift(unsigned char value, char up_flag)
/* kludge... */ /* kludge... */
if (value == KVAL(K_CAPSSHIFT)) { if (value == KVAL(K_CAPSSHIFT)) {
value = KVAL(K_SHIFT); value = KVAL(K_SHIFT);
if (!up_flag) { if (!up_flag)
clr_vc_kbd_led(kbd, VC_CAPSLOCK); clr_vc_kbd_led(kbd, VC_CAPSLOCK);
clr_vc_kbd_lock(kbd, VC_CAPSLOCK);
}
} }
if (up_flag) { if (up_flag) {
......
...@@ -931,9 +931,9 @@ static int write_chan(struct tty_struct * tty, struct file * file, ...@@ -931,9 +931,9 @@ static int write_chan(struct tty_struct * tty, struct file * file,
if (opost(c, tty) < 0) if (opost(c, tty) < 0)
break; break;
b++; nr--; b++; nr--;
}
if (tty->driver.flush_chars) if (tty->driver.flush_chars)
tty->driver.flush_chars(tty); tty->driver.flush_chars(tty);
}
} else { } else {
c = tty->driver.write(tty, 1, b, nr); c = tty->driver.write(tty, 1, b, nr);
b += c; b += c;
......
...@@ -1535,8 +1535,10 @@ int tty_register_driver(struct tty_driver *driver) ...@@ -1535,8 +1535,10 @@ int tty_register_driver(struct tty_driver *driver)
return 0; return 0;
error = register_chrdev(driver->major, driver->name, &tty_fops); error = register_chrdev(driver->major, driver->name, &tty_fops);
if (error) if (error < 0)
return error; return error;
else if(driver->major == 0)
driver->major = error;
if (!driver->put_char) if (!driver->put_char)
driver->put_char = tty_default_put_char; driver->put_char = tty_default_put_char;
...@@ -1545,7 +1547,7 @@ int tty_register_driver(struct tty_driver *driver) ...@@ -1545,7 +1547,7 @@ int tty_register_driver(struct tty_driver *driver)
driver->next = tty_drivers; driver->next = tty_drivers;
tty_drivers->prev = driver; tty_drivers->prev = driver;
tty_drivers = driver; tty_drivers = driver;
return 0; return error;
} }
/* /*
...@@ -1589,7 +1591,7 @@ long tty_init(long kmem_start) ...@@ -1589,7 +1591,7 @@ long tty_init(long kmem_start)
panic("size of tty structure > PAGE_SIZE!"); panic("size of tty structure > PAGE_SIZE!");
if (register_chrdev(TTY_MAJOR,"tty",&tty_fops)) if (register_chrdev(TTY_MAJOR,"tty",&tty_fops))
panic("unable to get major %d for tty device", TTY_MAJOR); 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); panic("unable to get major %d for tty device", TTYAUX_MAJOR);
kmem_start = kbd_init(kmem_start); kmem_start = kbd_init(kmem_start);
......
...@@ -193,6 +193,11 @@ ...@@ -193,6 +193,11 @@
* DIFFERENTIAL - if defined, NCR53c81 chips will use external differential * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
* transceivers. * 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. * LINKED - if defined, linked commands are supported.
* *
* PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases. * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
...@@ -1980,8 +1985,14 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) { ...@@ -1980,8 +1985,14 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance) {
if (!cmd->device->borken && if (!cmd->device->borken &&
(transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) { (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
#else #else
if (!cmd->device->borken && transfersize = cmd->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 % cmd->SCp.this_residual && !(cmd->SCp.this_residual %
transfersize)) { transfersize)) {
#endif #endif
......
...@@ -4,4 +4,5 @@ extern void print_command(unsigned char *); ...@@ -4,4 +4,5 @@ extern void print_command(unsigned char *);
extern int print_msg(unsigned char *); extern int print_msg(unsigned char *);
extern void print_sense(char *, Scsi_Cmnd *); extern void print_sense(char *, Scsi_Cmnd *);
extern void print_status(int); extern void print_status(int);
extern void print_Scsi_Cmnd (Scsi_Cmnd *);
#endif /* def _CONSTANTS_H */ #endif /* def _CONSTANTS_H */
#define AUTOSENSE #define AUTOSENSE
#define PSEUDO_DMA #define PSEUDO_DMA
#define FOO
#define UNSAFE /* Not unsafe for PAS16 -- use it */
/* /*
* This driver adapted from Drew Eckhardt's Trantor T128 driver * This driver adapted from Drew Eckhardt's Trantor T128 driver
...@@ -40,6 +42,11 @@ ...@@ -40,6 +42,11 @@
* AUTOSENSE - if defined, REQUEST SENSE will be performed automatically * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
* for commands that return with a CHECK CONDITION status. * 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 * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
* increase compared to polled I/O. * increase compared to polled I/O.
* *
...@@ -47,11 +54,13 @@ ...@@ -47,11 +54,13 @@
* *
* SCSI2 - enable support for SCSI-II tagged queueing. Untested. * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
* *
* * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This
* UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You * parameter comes from the NCR5380 code. It is NOT unsafe with
* only really want to use this if you're having a problem with * the PAS16 and you should use it. If you don't you will have
* dropped characters during high speed communications, and even * a problem with dropped characters during high speed
* then, you're going to be better off twiddling with transfersize. * 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. * 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 ) ...@@ -242,7 +251,7 @@ void init_board( unsigned short io_port, int irq, int force_irq )
int pas16_hw_detect( unsigned short board_num ) int pas16_hw_detect( unsigned short board_num )
{ {
unsigned char board_rev, tmp; 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 /* See if we can find a PAS16 board at the address associated
* with this logical board number. * with this logical board number.
...@@ -251,26 +260,39 @@ int pas16_hw_detect( unsigned short board_num ) ...@@ -251,26 +260,39 @@ int pas16_hw_detect( unsigned short board_num )
/* First, attempt to take a newer model board out of reset and /* First, attempt to take a newer model board out of reset and
* give it a base address. This shouldn't affect older boards. * 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 */ /* 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 ) if( board_rev == 0xff )
return 0; return 0;
tmp = board_rev ^ 0xe0; tmp = board_rev ^ 0xe0;
outb( tmp, port + PCB_CONFIG ); outb( tmp, io_port + PCB_CONFIG );
tmp = inb( port + PCB_CONFIG ); tmp = inb( io_port + PCB_CONFIG );
outb( board_rev, port + PCB_CONFIG ); outb( board_rev, io_port + PCB_CONFIG );
if( board_rev != tmp ) /* Not a PAS-16 */ if( board_rev != tmp ) /* Not a PAS-16 */
return 0; 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 */ 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; return 1;
} }
...@@ -426,7 +448,15 @@ int pas16_biosparam(Disk * disk, int dev, int * ip) ...@@ -426,7 +448,15 @@ int pas16_biosparam(Disk * disk, int dev, int * ip)
int size = disk->capacity; int size = disk->capacity;
ip[0] = 64; ip[0] = 64;
ip[1] = 32; 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; return 0;
} }
......
...@@ -628,6 +628,19 @@ are any multiple of 512 bytes long. */ ...@@ -628,6 +628,19 @@ are any multiple of 512 bytes long. */
}; };
#endif #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; SCpnt->this_count = this_count;
scsi_do_cmd (SCpnt, (void *) cmd, buffer, scsi_do_cmd (SCpnt, (void *) cmd, buffer,
realcount * scsi_CDs[dev].sector_size, realcount * scsi_CDs[dev].sector_size,
......
...@@ -316,10 +316,7 @@ int ext2_new_block (struct super_block * sb, unsigned long goal, ...@@ -316,10 +316,7 @@ int ext2_new_block (struct super_block * sb, unsigned long goal,
else else
lmap |= 0xffffffff << (31 - (j & 31)); lmap |= 0xffffffff << (31 - (j & 31));
if (lmap != 0xffffffffl) { if (lmap != 0xffffffffl) {
__asm__ ("bsfl %1,%0" k = ffz(lmap) + 1;
: "=r" (k)
: "r" (~lmap));
k++;
if ((j + k) < EXT2_BLOCKS_PER_GROUP(sb)) { if ((j + k) < EXT2_BLOCKS_PER_GROUP(sb)) {
j += k; j += k;
goto got_block; goto got_block;
......
...@@ -121,4 +121,16 @@ extern inline int find_next_zero_bit (unsigned long * addr, int size, ...@@ -121,4 +121,16 @@ extern inline int find_next_zero_bit (unsigned long * addr, int size,
return (offset + set + res); 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 */ #endif /* _I386_BITOPS_H */
#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
...@@ -20,6 +20,7 @@ struct linger { ...@@ -20,6 +20,7 @@ struct linger {
#define SOCK_RAW 3 /* raw socket */ #define SOCK_RAW 3 /* raw socket */
#define SOCK_RDM 4 /* reliably-delivered message */ #define SOCK_RDM 4 /* reliably-delivered message */
#define SOCK_SEQPACKET 5 /* sequential packet socket */ #define SOCK_SEQPACKET 5 /* sequential packet socket */
#define SOCK_NCP 6 /* Novell NCP socket */
#define SOCK_PACKET 10 /* linux specific way of */ #define SOCK_PACKET 10 /* linux specific way of */
/* getting packets at the dev */ /* getting packets at the dev */
/* level. For writing rarp and */ /* level. For writing rarp and */
......
...@@ -177,7 +177,8 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait) ...@@ -177,7 +177,8 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait)
switch(sel_type) switch(sel_type)
{ {
case SEL_IN: 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 */ /* Connection closed: Wake up */
return(1); return(1);
......
...@@ -56,8 +56,16 @@ ...@@ -56,8 +56,16 @@
#include <linux/termios.h> /* For TIOCOUTQ/INQ */ #include <linux/termios.h> /* For TIOCOUTQ/INQ */
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include "p8022.h" #include "p8022.h"
#include "ncp.h"
#ifdef CONFIG_IPX #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. * * Handlers for the socket list. *
...@@ -75,7 +83,9 @@ static ipx_socket *volatile ipx_socket_list=NULL; ...@@ -75,7 +83,9 @@ static ipx_socket *volatile ipx_socket_list=NULL;
static void ipx_remove_socket(ipx_socket *sk) static void ipx_remove_socket(ipx_socket *sk)
{ {
ipx_socket *s; ipx_socket *s;
unsigned long flags;
save_flags(flags);
cli(); cli();
s=ipx_socket_list; s=ipx_socket_list;
if(s==sk) if(s==sk)
...@@ -89,20 +99,23 @@ static void ipx_remove_socket(ipx_socket *sk) ...@@ -89,20 +99,23 @@ static void ipx_remove_socket(ipx_socket *sk)
if(s->next==sk) if(s->next==sk)
{ {
s->next=sk->next; s->next=sk->next;
sti(); restore_flags(flags);
return; return;
} }
s=s->next; s=s->next;
} }
sti(); restore_flags(flags);
} }
static void ipx_insert_socket(ipx_socket *sk) static void ipx_insert_socket(ipx_socket *sk)
{ {
unsigned long flags;
save_flags(flags);
cli(); cli();
sk->next=ipx_socket_list; sk->next=ipx_socket_list;
ipx_socket_list=sk; ipx_socket_list=sk;
sti(); restore_flags(flags);
} }
static ipx_socket *ipx_find_socket(int port) static ipx_socket *ipx_find_socket(int port)
...@@ -133,9 +146,10 @@ static void ipx_destroy_socket(ipx_socket *sk) ...@@ -133,9 +146,10 @@ static void ipx_destroy_socket(ipx_socket *sk)
ipx_remove_socket(sk); ipx_remove_socket(sk);
while((skb=skb_dequeue(&sk->receive_queue))!=NULL) while((skb=skb_dequeue(&sk->receive_queue))!=NULL)
{
kfree_skb(skb,FREE_READ); kfree_skb(skb,FREE_READ);
}
while((skb=skb_dequeue(&sk->write_queue))!=NULL)
kfree_skb(skb,FREE_WRITE);
kfree_s(sk,sizeof(*sk)); kfree_s(sk,sizeof(*sk));
} }
...@@ -151,20 +165,24 @@ int ipx_get_info(char *buffer, char **start, off_t offset, int length) ...@@ -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 */ /* 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) for (s = ipx_socket_list; s != NULL; s = s->next)
{ {
len += sprintf (buffer+len,"%02X ", s->ipx_type); 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,"%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[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], s->ipx_source_addr.node[3], s->ipx_source_addr.node[4], s->ipx_source_addr.node[5],
htons(s->ipx_source_addr.sock)); 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[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], s->ipx_dest_addr.node[3], s->ipx_dest_addr.node[4], s->ipx_dest_addr.node[5],
htons(s->ipx_dest_addr.sock)); htons(s->ipx_dest_addr.sock));
len += sprintf (buffer+len,"%08lX:%08lX ", s->wmem_alloc, s->rmem_alloc); 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 */ /* Are we still dumping unwanted data then discard the record */
pos=begin+len; pos=begin+len;
...@@ -566,20 +584,56 @@ static void def_callback2(struct sock *sk, int len) ...@@ -566,20 +584,56 @@ static void def_callback2(struct sock *sk, int len)
wake_up_interruptible(sk->sleep); 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; ipx_packet *ipx;
sk=(ipx_socket *)kmalloc(sizeof(*sk),GFP_KERNEL); struct sk_buff *skb;
if(sk==NULL) char *data;
return(-ENOMEM);
switch(sock->type) 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: struct ncp_request_sf req;
break;
default: req.func=0x15;
kfree_s((void *)sk,sizeof(*sk)); req.s_func=0x01;
return(-ESOCKTNOSUPPORT); 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->dead=0;
sk->next=NULL; sk->next=NULL;
sk->broadcast=0; sk->broadcast=0;
...@@ -598,7 +652,6 @@ static int ipx_create(struct socket *sock, int protocol) ...@@ -598,7 +652,6 @@ static int ipx_create(struct socket *sock, int protocol)
skb_queue_head_init(&sk->back_log); skb_queue_head_init(&sk->back_log);
sk->state=TCP_CLOSE; sk->state=TCP_CLOSE;
sk->socket=sock; sk->socket=sock;
sk->type=sock->type;
sk->ipx_type=0; /* General user level IPX */ sk->ipx_type=0; /* General user level IPX */
sk->debug=0; sk->debug=0;
...@@ -610,14 +663,76 @@ static int ipx_create(struct socket *sock, int protocol) ...@@ -610,14 +663,76 @@ static int ipx_create(struct socket *sock, int protocol)
{ {
sock->data=(void *)sk; sock->data=(void *)sk;
sk->sleep=sock->wait; sk->sleep=sock->wait;
sk->type=sock->type;
} }
else
sk->type=SOCK_DGRAM;
sk->priority=SOPRI_NORMAL;
sk->state_change=def_callback1; sk->state_change=def_callback1;
sk->data_ready=def_callback2; sk->data_ready=def_callback2;
sk->write_space=def_callback1; sk->write_space=def_callback1;
sk->error_report=def_callback1; sk->error_report=def_callback1;
sk->zapped=1; 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); return(0);
} }
...@@ -633,40 +748,72 @@ static int ipx_release(struct socket *sock, struct socket *peer) ...@@ -633,40 +748,72 @@ static int ipx_release(struct socket *sock, struct socket *peer)
return(0); return(0);
if(!sk->dead) if(!sk->dead)
sk->state_change(sk); sk->state_change(sk);
sk->dead=1;
sock->data=NULL; sock->data=NULL;
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); ipx_destroy_socket(sk);
}
return(0); return(0);
} }
static unsigned short first_free_socketnum(void) static unsigned short first_free_socketnum(void)
{ {
static unsigned short socketNum = 0x4000; static unsigned short socketNum = 0x3fff;
while (ipx_find_socket(htons(socketNum)) != NULL) while (ipx_find_socket(htons(++socketNum)) != NULL)
if (socketNum > 0x7ffc) socketNum = 0x4000; if (socketNum > 0x7ffc) socketNum = 0x3fff;
return htons(socketNum++); return htons(socketNum);
} }
static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) 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; struct ipx_route *rt;
unsigned char *nodestart; unsigned char *nodestart;
struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr; struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr;
sk=(ipx_socket *)sock->data;
if(sk->zapped==0) if(sk->zapped==0)
return(-EIO); return(-EIO);
if(addr_len!=sizeof(struct sockaddr_ipx)) if(addr_len!=sizeof(struct sockaddr_ipx))
return -EINVAL; return -EINVAL;
if (addr->sipx_port == 0) if (addr->sipx_port == 0)
{ {
addr->sipx_port = first_free_socketnum(); 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) if (addr->sipx_port == 0)
return -EINVAL; return -EINVAL;
} }
...@@ -685,6 +832,19 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) ...@@ -685,6 +832,19 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
return -EADDRINUSE; 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; sk->ipx_source_addr.sock=addr->sipx_port;
if (addr->sipx_network == 0L) if (addr->sipx_network == 0L)
...@@ -705,6 +865,7 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len) ...@@ -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_source_addr.net=rt->net;
sk->ipx_type=addr->sipx_type;
/* IPX addresses zero pad physical addresses less than 6 */ /* IPX addresses zero pad physical addresses less than 6 */
memset(sk->ipx_source_addr.node,'\0',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) ...@@ -713,41 +874,102 @@ static int ipx_bind(struct socket *sock, struct sockaddr *uaddr,int addr_len)
ipx_insert_socket(sk); ipx_insert_socket(sk);
sk->zapped=0; 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) if(sk->debug)
printk("IPX: socket is bound.\n"); printk("IPX: socket is bound.\n");
return(0); 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, static int ipx_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags) int addr_len, int flags)
{ {
ipx_socket *sk=(ipx_socket *)sock->data; ipx_socket *sk=(ipx_socket *)sock->data;
struct sockaddr_ipx *addr; struct sockaddr_ipx *addr=(struct sockaddr_ipx *)uaddr;
sk->state = TCP_CLOSE; sk->state = TCP_CLOSE;
sock->state = SS_UNCONNECTED; sock->state = SS_UNCONNECTED;
if(addr_len!=sizeof(addr)) if(addr_len!=sizeof(struct sockaddr_ipx))
return(-EINVAL); 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 */ /* put the autobinding in */
{ {
struct sockaddr_ipx uaddr;
int ret; int ret;
struct sockaddr_ipx addr;
uaddr.sipx_port = 0; addr.sipx_type = 0;
uaddr.sipx_network = 0L; addr.sipx_port = 0;
ret = ipx_bind (sock, (struct sockaddr *)&uaddr, sizeof(struct sockaddr_ipx)); addr.sipx_network = 0L;
ret = ipx_bind (sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_ipx));
if (ret != 0) return (ret); if (ret != 0) return (ret);
} }
sk->ipx_dest_addr.net=addr->sipx_network; sk->ipx_dest_addr.net=addr->sipx_network;
sk->ipx_dest_addr.sock=addr->sipx_port; sk->ipx_dest_addr.sock=addr->sipx_port;
memcpy(sk->ipx_dest_addr.node,addr->sipx_node,sizeof(sk->ipx_source_addr.node)); 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) if(ipxrtr_get_dev(sk->ipx_dest_addr.net)==NULL)
return -ENETUNREACH; return -ENETUNREACH;
if (sk->type == SOCK_NCP)
return(ncp_connect(sock, sk));
sk->ipx_type=addr->sipx_type;
sock->state = SS_CONNECTED; sock->state = SS_CONNECTED;
sk->state=TCP_ESTABLISHED; sk->state=TCP_ESTABLISHED;
return(0); return(0);
...@@ -770,9 +992,8 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, ...@@ -770,9 +992,8 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
{ {
ipx_address *addr; ipx_address *addr;
struct sockaddr_ipx sipx; struct sockaddr_ipx sipx;
ipx_socket *sk; ipx_socket *sk=(ipx_socket *)sock->data;
sk=(ipx_socket *)sock->data;
*uaddr_len = sizeof(struct sockaddr_ipx); *uaddr_len = sizeof(struct sockaddr_ipx);
...@@ -783,7 +1004,21 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, ...@@ -783,7 +1004,21 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
addr=&sk->ipx_dest_addr; addr=&sk->ipx_dest_addr;
} }
else 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; addr=&sk->ipx_source_addr;
}
sipx.sipx_family = AF_IPX; sipx.sipx_family = AF_IPX;
sipx.sipx_port = addr->sock; sipx.sipx_port = addr->sock;
...@@ -793,79 +1028,484 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr, ...@@ -793,79 +1028,484 @@ static int ipx_getname(struct socket *sock, struct sockaddr *uaddr,
return(0); return(0);
} }
int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) static int ipx_build_header(ipx_address *ipx, struct sk_buff *skb,
ipx_socket *sk, int len, unsigned char type)
{ {
/* NULL here for pt means the packet was looped back */ ipx_packet *ipx_pack;
ipx_socket *sock;
ipx_packet *ipx;
ipx_route *rt; ipx_route *rt;
ipx_route *ln; struct datalink_proto *dl = NULL;
unsigned char IPXaddr[6]; unsigned char IPXaddr[6];
int self_addressing = 0;
int broadcast = 0;
ipx=(ipx_packet *)skb->h.raw; if(sk->debug)
printk("IPX: build_header: Addresses built.\n");
if(ipx->ipx_checksum!=IPX_NO_CHECKSUM) if(memcmp(&ipx->node,&ipx_broadcast_node,6)==0)
{ {
/* We don't do checksum options. We can't really. Novell don't seem to have documented them. if (!sk->broadcast)
If you need them try the XNS checksum since IPX is basically XNS in disguise. It might be return -ENETUNREACH;
the same... */ broadcast = 1;
kfree_skb(skb,FREE_READ);
return(0);
} }
/* Too small */ /* Build a packet */
if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet))
{
kfree_skb(skb,FREE_READ);
return(0);
}
/* Too many hops */ if(sk->debug)
if(ipx->ipx_tctrl>16) printk("IPX: build_header: building packet.\n");
{
kfree_skb(skb,FREE_READ);
return(0);
}
/* Determine what local ipx endpoint this is */ /* Find out where this has to go */
ln = ipxrtr_get_local_net(dev, pt->type); if (ipx->net == 0L) {
if (ln == NULL) rt = ipxrtr_get_default_net();
if (rt != NULL)
ipx->net = rt->net;
} else
rt=ipxrtr_get_dev(ipx->net);
if(rt==NULL)
{ {
kfree_skb(skb,FREE_READ); return -ENETUNREACH;
return(0);
} }
memset(IPXaddr, '\0', 6); dl=rt->datalink;
memcpy(IPXaddr+(6 - dev->addr_len), dev->dev_addr, dev->addr_len);
/* Not us/broadcast */ skb->mem_addr=skb;
if(memcmp(IPXaddr,ipx->ipx_dest.node,6)!=0 skb->sk=sk;
&& memcmp(ipx_broadcast_node,ipx->ipx_dest.node,6)!=0) skb->free=1;
{ skb->arp=1;
/********************************************************************************************** skb->tries=0;
IPX router. Roughly as per the Novell spec. This doesn't handle netbios flood fill if(sk->debug)
broadcast frames. See the Novell IPX router specification for more details printk("Building MAC header.\n");
(for ftp from ftp.novell.com) skb->dev=rt->dev;
***********************************************************************************************/ /* Build Data Link header */
dl->datalink_header(dl, skb,
(rt->flags&IPX_RT_ROUTED)?rt->router_node:ipx->node);
int incoming_size; /* See if we are sending to ourself */
int outgoing_size; memset(IPXaddr, '\0', 6);
struct sk_buff *skb2; memcpy(IPXaddr+(6 - skb->dev->addr_len), skb->dev->dev_addr,
int free_it=0; skb->dev->addr_len);
/* Rule: Don't forward packets that have exceeded the hop limit. This is fixed at 16 in IPX */ self_addressing = !memcmp(IPXaddr,
if(ipx->ipx_tctrl==16) (rt->flags&IPX_RT_ROUTED)?rt->router_node
{ :ipx->node,
kfree_skb(skb,FREE_READ); 6);
return(0);
}
ipx->ipx_tctrl++; /* Now the IPX */
/* Don't forward if we don't have a route. We ought to go off and start hunting out routes but if(sk->debug)
if someone needs this _THEY_ can add it */ printk("Building IPX Header.\n");
rt=ipxrtr_get_dev(ipx->ipx_dest.net); 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 */
ipx_socket *sock;
ipx_packet *ipx;
ipx_route *rt;
ipx_route *ln;
unsigned char IPXaddr[6];
ipx=(ipx_packet *)skb->h.raw;
if(ipx->ipx_checksum!=IPX_NO_CHECKSUM)
{
/* We don't do checksum options. We can't really. Novell don't seem to have documented them.
If you need them try the XNS checksum since IPX is basically XNS in disguise. It might be
the same... */
kfree_skb(skb,FREE_READ);
return(0);
}
/* Too small */
if(htons(ipx->ipx_pktsize)<sizeof(ipx_packet))
{
kfree_skb(skb,FREE_READ);
return(0);
}
/* Too many hops */
if(ipx->ipx_tctrl>16)
{
kfree_skb(skb,FREE_READ);
return(0);
}
/* Determine what local ipx endpoint this is */
ln = ipxrtr_get_local_net(dev, pt->type);
if (ln == NULL)
{
kfree_skb(skb,FREE_READ);
return(0);
}
memset(IPXaddr, '\0', 6);
memcpy(IPXaddr+(6 - dev->addr_len), dev->dev_addr, dev->addr_len);
/* Not us/broadcast */
if(memcmp(IPXaddr,ipx->ipx_dest.node,6)!=0
&& memcmp(ipx_broadcast_node,ipx->ipx_dest.node,6)!=0)
{
/**********************************************************************************************
IPX router. Roughly as per the Novell spec. This doesn't handle netbios flood fill
broadcast frames. See the Novell IPX router specification for more details
(for ftp from ftp.novell.com)
***********************************************************************************************/
int incoming_size;
int outgoing_size;
struct sk_buff *skb2;
int free_it=0;
/* Rule: Don't forward packets that have exceeded the hop limit. This is fixed at 16 in IPX */
if(ipx->ipx_tctrl==16)
{
kfree_skb(skb,FREE_READ);
return(0);
}
ipx->ipx_tctrl++;
/* Don't forward if we don't have a route. We ought to go off and start hunting out routes but
if someone needs this _THEY_ can add it */
rt=ipxrtr_get_dev(ipx->ipx_dest.net);
if(rt==NULL) /* Unlike IP we can send on the interface we received. Eg doing DIX/802.3 conversion */ if(rt==NULL) /* Unlike IP we can send on the interface we received. Eg doing DIX/802.3 conversion */
{ {
kfree_skb(skb,FREE_READ); kfree_skb(skb,FREE_READ);
...@@ -943,6 +1583,9 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) ...@@ -943,6 +1583,9 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
return(0); return(0);
} }
if (sock->type == SOCK_NCP)
return (ncp_rcv(sock, skb));
sock->rmem_alloc+=skb->mem_len; sock->rmem_alloc+=skb->mem_len;
skb->sk = sock; skb->sk = sock;
...@@ -952,65 +1595,27 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt) ...@@ -952,65 +1595,27 @@ int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
return(0); return(0);
} }
static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock, static int ipx_do_sendto(ipx_socket *sk, ipx_address *ipx,
unsigned flags, struct sockaddr *usip, int addr_len) 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 sk_buff *skb;
struct device *dev; struct device *dev;
struct ipx_packet *ipx; ipx_packet *ipx_pack;
int size; int size;
ipx_route *rt; ipx_route *rt;
struct datalink_proto *dl = NULL; 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 <sizeof(*usipx))
return(-EINVAL);
if(usipx->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) if(sk->debug)
printk("IPX: sendto: Addresses built.\n"); 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) if (!sk->broadcast)
return -ENETUNREACH; return -ENETUNREACH;
broadcast = 1;
} }
/* Build a packet */ /* Build a packet */
...@@ -1021,12 +1626,12 @@ static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock, ...@@ -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 */ size=sizeof(ipx_packet)+len; /* For mac headers */
/* Find out where this has to go */ /* Find out where this has to go */
if (usipx->sipx_network == 0L) { if (ipx->net == 0L) {
rt = ipxrtr_get_default_net(); rt = ipxrtr_get_default_net();
if (rt != NULL) if (rt != NULL)
usipx->sipx_network = rt->net; ipx->net = rt->net;
} else } else
rt=ipxrtr_get_dev(usipx->sipx_network); rt=ipxrtr_get_dev(ipx->net);
if(rt==NULL) if(rt==NULL)
{ {
...@@ -1046,87 +1651,118 @@ static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock, ...@@ -1046,87 +1651,118 @@ static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
return -EAGAIN; return -EAGAIN;
} }
if (flag)
skb=alloc_skb(size,GFP_KERNEL); skb=alloc_skb(size,GFP_KERNEL);
else
skb=alloc_skb(size,GFP_ATOMIC);
if(skb==NULL) if(skb==NULL)
return -ENOMEM; return -ENOMEM;
skb->mem_addr=skb; sk->wmem_alloc+=skb->mem_len;
skb->sk=sk;
skb->free=1;
skb->arp=1;
skb->len=size; skb->len=size;
sk->wmem_alloc+=skb->mem_len; ipx_build_header(ipx, skb, sk, len, type);
if(sk->debug) ipx_pack = (ipx_packet *)(skb->h.raw);
printk("Building MAC header.\n");
skb->dev=rt->dev;
/* Build Data Link header */ /* User data follows immediately after the IPX data */
dl->datalink_header(dl, skb, if (flag)
(rt->flags&IPX_RT_ROUTED)?rt->router_node:usipx->sipx_node); memcpy_fromfs((char *)(ipx_pack+1),ubuf,len);
else
memcpy((char *)(ipx_pack+1),ubuf,len);
/* See if we are sending to ourself */ if (sk->type == SOCK_NCP)
memset(IPXaddr, '\0', 6); {
memcpy(IPXaddr+(6 - skb->dev->addr_len), skb->dev->dev_addr, struct ncp_request *req=(struct ncp_request *)(ipx_pack+1);
skb->dev->addr_len);
self_addressing = !memcmp(IPXaddr, switch (sk->state)
(rt->flags&IPX_RT_ROUTED)?rt->router_node {
:usipx->sipx_node, case TCP_SYN_SENT:
6); 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) if(sk->debug)
printk("IPX: Transmitting buffer\n"); printk("IPX: Transmitting buffer\n");
if((dev->flags&IFF_LOOPBACK) || self_addressing) {
struct packet_type pt;
/* loop back */ ipx_xmit(skb);
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 */ return len;
pt.type = rt->dlink_type; }
skb2=alloc_skb(skb->len, GFP_ATOMIC); static int ipx_sendto(struct socket *sock, void *ubuf, int len, int noblock,
skb2->mem_addr=skb2; unsigned flags, struct sockaddr *usip, int addr_len)
skb2->free=1; {
skb2->arp=1; ipx_socket *sk=(ipx_socket *)sock->data;
skb2->len=skb->len; ipx_address ipx;
skb2->sk = NULL; struct sockaddr_ipx *usipx=(struct sockaddr_ipx *)usip;
skb2->h.raw = skb2->data + rt->datalink->header_length struct sockaddr_ipx local_sipx;
+ dev->hard_header_len;
memcpy(skb2->data, skb->data, skb->len); if(flags)
ipx_rcv(skb2,dev,&pt); 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 <sizeof(*usipx))
return(-EINVAL);
if(usipx->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) static int ipx_send(struct socket *sock, void *ubuf, int size, int noblock, unsigned flags)
{ {
return ipx_sendto(sock,ubuf,size,noblock,flags,NULL,0); return ipx_sendto(sock,ubuf,size,noblock,flags,NULL,0);
...@@ -1170,12 +1806,14 @@ static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock, ...@@ -1170,12 +1806,14 @@ static int ipx_recvfrom(struct socket *sock, void *ubuf, int size, int noblock,
sipx->sipx_type = ipx->ipx_type; sipx->sipx_type = ipx->ipx_type;
} }
skb_free_datagram(skb); skb_free_datagram(skb);
return(copied); return(copied);
} }
static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock) static int ipx_write(struct socket *sock, char *ubuf, int size, int noblock)
{ {
return ipx_send(sock,ubuf,size,noblock,0); return ipx_send(sock,ubuf,size,noblock,0);
} }
......
/*
*
* Kernel support for NCP
*
* Mark Evans 1994
*
*/
#ifndef _NCP_H
#define _NCP_H
#include <linux/ncp.h>
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 */
...@@ -41,6 +41,7 @@ ...@@ -41,6 +41,7 @@
#endif #endif
#ifdef CONFIG_IPX #ifdef CONFIG_IPX
#include "ipx.h" #include "ipx.h"
#include "ncp.h"
#endif #endif
#define SOCK_ARRAY_SIZE 64 #define SOCK_ARRAY_SIZE 64
...@@ -137,6 +138,7 @@ struct sock { ...@@ -137,6 +138,7 @@ struct sock {
#ifdef CONFIG_IPX #ifdef CONFIG_IPX
ipx_address ipx_source_addr,ipx_dest_addr; ipx_address ipx_source_addr,ipx_dest_addr;
unsigned short ipx_type; unsigned short ipx_type;
struct ncp_info ncp;
#endif #endif
#ifdef CONFIG_AX25 #ifdef CONFIG_AX25
/* Really we want to add a per protocol private area */ /* Really we want to add a per protocol private area */
......
...@@ -592,7 +592,8 @@ static int sock_socket(int family, int type, int protocol) ...@@ -592,7 +592,8 @@ static int sock_socket(int family, int type, int protocol)
if ((type != SOCK_STREAM && type != SOCK_DGRAM && if ((type != SOCK_STREAM && type != SOCK_DGRAM &&
type != SOCK_SEQPACKET && type != SOCK_RAW && type != SOCK_SEQPACKET && type != SOCK_RAW &&
type != SOCK_PACKET) || protocol < 0) type != SOCK_PACKET && type != SOCK_NCP)
|| protocol < 0)
return(-EINVAL); return(-EINVAL);
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment