Commit 36f4514a authored by Linus Torvalds's avatar Linus Torvalds

Import 0.99.14p

parent ddd9ed00
VERSION = 0.99
PATCHLEVEL = 14
ALPHA = o
ALPHA = p
all: Version zImage
......
......@@ -4,14 +4,15 @@
#DEBUG = -DDEBUGGING
DEBUG =
PARANOID = -DPARANOID
REENTRANT = -DREENTRANT_FPU
CFLAGS := $(CFLAGS) -DPARANOID $(DEBUG) -fno-builtin
CFLAGS := $(CFLAGS) $(PARANOID) $(DEBUG) -fno-builtin
.c.o:
$(CC) $(CFLAGS) $(MATH_EMULATION) -c $<
.S.o:
$(CC) -D__ASSEMBLER__ $(REENTRANT) -c $<
$(CC) -D__ASSEMBLER__ $(PARANOID) $(REENTRANT) -c $<
.s.o:
$(CC) -c $<
......
+---------------------------------------------------------------------------+
| wm-FPU-emu an FPU emulator for 80386 and 80486SX microprocessors. |
| |
| Copyright (C) 1992,1993 |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
......@@ -47,7 +47,7 @@ Please report bugs, etc to me at:
--Bill Metzenthen
Sept 1993
Jan 1994
----------------------- Internals of wm-FPU-emu -----------------------
......@@ -305,7 +305,8 @@ Daniel Carosone, danielce@ee.mu.oz.au
cae@jpmorgan.com
Hamish Coleman, t933093@minyos.xx.rmit.oz.au
Bruce Evans, bde@kralizec.zeta.org.au
Timo Korvola, Timo.Korvola@hut.fi
...and numerous others who responded to my request for help with
a real 80486.
......@@ -3,7 +3,7 @@
| |
| The error handling functions for wm-FPU-emu |
| |
| Copyright (C) 1992,1993 |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
......@@ -208,7 +208,7 @@ static struct {
0x125 in fpu_trig.c
0x126 in fpu_entry.c
0x127 in poly_2xm1.c
0x2nn in an *.s file:
0x2nn in an *.S file:
0x201 in reg_u_add.S, reg_round.S
0x202 in reg_u_div.S
0x203 in reg_u_div.S
......@@ -227,6 +227,8 @@ static struct {
0x216 in reg_round.S
0x217 in reg_round.S
0x218 in reg_round.S
0x220 in reg_norm.S
0x221 in reg_norm.S
*/
void exception(int n)
......
......@@ -3,7 +3,7 @@
| |
| Implementation of the FPU "transcendental" functions. |
| |
| Copyright (C) 1992,1993 |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
......@@ -88,9 +88,19 @@ static int trig_arg(FPU_REG *X, int even)
128 bits precision. */
significand(&tmp) = q + 1;
tmp.exp = EXP_BIAS + 63;
tmp.tag = TW_Valid;
normalize(&tmp);
reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
reg_add(X, &tmp, X, FULL_PRECISION);
if ( X->sign == SIGN_NEG )
{
/* CONST_PI2extra is negative, so the result of the addition
can be negative. This means that the argument is actually
in a different quadrant. The correction is always < pi/2,
so it can't overflow into yet another quadrant. */
X->sign = SIGN_POS;
q++;
}
}
#endif BETTER_THAN_486
}
......@@ -107,9 +117,23 @@ static int trig_arg(FPU_REG *X, int even)
128 bits precision. */
significand(&tmp) = q;
tmp.exp = EXP_BIAS + 63;
tmp.tag = TW_Valid;
normalize(&tmp);
reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
reg_sub(X, &tmp, X, FULL_PRECISION);
if ( (X->exp == CONST_PI2.exp) &&
((X->sigh > CONST_PI2.sigh)
|| ((X->sigh == CONST_PI2.sigh)
&& (X->sigl > CONST_PI2.sigl))) )
{
/* CONST_PI2extra is negative, so the result of the
subtraction can be larger than pi/2. This means
that the argument is actually in a different quadrant.
The correction is always < pi/2, so it can't overflow
into yet another quadrant. */
reg_sub(&CONST_PI, X, X, FULL_PRECISION);
q++;
}
}
}
#endif BETTER_THAN_486
......
/*---------------------------------------------------------------------------+
| reg_norm.S |
| |
| Copyright (C) 1992,1993 |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
......@@ -29,6 +29,17 @@ _normalize:
movl PARAM1,%ebx
#ifdef PARANOID
cmpb TW_Valid,TAG(%ebx)
je L_ok
pushl $0x220
call _exception
addl $4,%esp
L_ok:
#endif PARANOID
movl SIGH(%ebx),%edx
movl SIGL(%ebx),%eax
......@@ -98,6 +109,17 @@ _normalize_nuo:
movl PARAM1,%ebx
#ifdef PARANOID
cmpb TW_Valid,TAG(%ebx)
je L_ok_nuo
pushl $0x221
call _exception
addl $4,%esp
L_ok_nuo:
#endif PARANOID
movl SIGH(%ebx),%edx
movl SIGL(%ebx),%eax
......
......@@ -2,12 +2,12 @@
| version.h |
| |
| |
| Copyright (C) 1992,1993 |
| Copyright (C) 1992,1993,1994 |
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
| Australia. E-mail billm@vaxc.cc.monash.edu.au |
| |
| |
+---------------------------------------------------------------------------*/
#define FPU_VERSION "wm-FPU-emu version BETA 1.6"
#define FPU_VERSION "wm-FPU-emu version Beta 1.7"
......@@ -80,11 +80,13 @@
#define MAJOR_NR CDU31A_CDROM_MAJOR
#include "blk.h"
#define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10
static unsigned short cdu31a_addresses[] =
{
0x340, /* Standard configuration Sony Interface */
0x1f88, /* Fusion CD-16 */
0x230, /* SoundBlaster 16 card */
0x360, /* Secondary standard Sony Interface */
0x320, /* Secondary standard Sony Interface */
0x330, /* Secondary standard Sony Interface */
......@@ -96,6 +98,13 @@ static int handle_sony_cd_attention(void);
static int read_subcode(void);
static void sony_get_toc(void);
static int scd_open(struct inode *inode, struct file *filp);
static void do_sony_cd_cmd(unsigned char cmd,
unsigned char *params,
unsigned int num_params,
unsigned char *result_buffer,
unsigned int *result_size);
static void size_to_buf(unsigned int size,
unsigned char *buf);
/* The base I/O address of the Sony Interface. This is a variable (not a
......@@ -116,7 +125,6 @@ static volatile unsigned short sony_cd_read_reg;
static volatile unsigned short sony_cd_fifost_reg;
static int initialized = 0; /* Has the drive been initialized? */
static int sony_disc_changed = 1; /* Has the disk been changed
since the last check? */
static int sony_toc_read = 0; /* Has the table of contents been
......@@ -306,6 +314,71 @@ write_cmd(unsigned char cmd)
outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg);
}
/*
* Set the drive parameters so the drive will auto-spin-up when a
* disk is inserted.
*/
static void
set_drive_params(void)
{
unsigned char res_reg[2];
unsigned int res_size;
unsigned char params[3];
params[0] = SONY_SD_MECH_CONTROL;
params[1] = 0x03;
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
params,
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
{
printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
}
}
/*
* This code will reset the drive and attempt to restore sane parameters.
*/
static void
restart_on_error(void)
{
unsigned char res_reg[2];
unsigned int res_size;
unsigned int retry_count;
printk("cdu31a: Resetting drive on error\n");
reset_drive();
retry_count = jiffies + SONY_RESET_TIMEOUT;
while ((retry_count > jiffies) && (!is_attention()))
{
sony_sleep();
}
set_drive_params();
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
{
printk("cdu31a: Unable to spin up drive: 0x%2.2x\n", res_reg[1]);
}
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 200;
schedule();
do_sony_cd_cmd(SONY_READ_TOC_CMD, NULL, 0, res_reg, &res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
{
printk("cdu31a: Unable to read TOC: 0x%2.2x\n", res_reg[1]);
}
sony_get_toc();
if (!sony_toc_read)
{
printk("cdu31a: Unable to get TOC data\n");
}
}
/*
* This routine writes data to the parameter register. Since this should
......@@ -459,6 +532,39 @@ get_result(unsigned char *result_buffer,
}
}
/*
* Read in a 2048 byte block of data.
*/
static void
read_data_block(unsigned char *data,
unsigned char *result_buffer,
unsigned int *result_size)
{
int i;
unsigned int retry_count;
for (i=0; i<2048; i++)
{
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!is_data_requested()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!is_data_requested())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
return;
}
*data = read_data_register();
data++;
}
}
/*
* This routine issues a read data command and gets the data. I don't
......@@ -469,35 +575,37 @@ get_result(unsigned char *result_buffer,
* received at any time and should be handled immediately (at least
* between every 2048 byte block) to check for errors, we can't wait
* until all the data is read.
*
* This routine returns the total number of sectors read. It will
* not return an error if it reads at least one sector successfully.
*/
static void
get_data(unsigned char *data,
static unsigned int
get_data(unsigned char *orig_data,
unsigned char *params, /* 6 bytes with the MSF start address
and number of sectors to read. */
unsigned int data_size,
unsigned int orig_data_size,
unsigned char *result_buffer,
unsigned int *result_size)
{
int i;
unsigned int cur_offset;
unsigned int retry_count;
int result_read;
int num_retries;
unsigned int num_sectors_read = 0;
unsigned char *data = orig_data;
unsigned int data_size = orig_data_size;
cli();
if (current != has_cd_task) /* Allow recursive calls to this routine */
while (sony_inuse)
{
while (sony_inuse)
interruptible_sleep_on(&sony_wait);
if (current->signal & ~current->blocked)
{
interruptible_sleep_on(&sony_wait);
if (current->signal & ~current->blocked)
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_SIGNAL_OP_ERR;
*result_size = 2;
return;
}
result_buffer[0] = 0x20;
result_buffer[1] = SONY_SIGNAL_OP_ERR;
*result_size = 2;
return 0;
}
}
sony_inuse = 1;
......@@ -506,6 +614,8 @@ get_data(unsigned char *data,
num_retries = 0;
retry_data_operation:
result_buffer[0] = 0;
result_buffer[1] = 0;
/*
* Clear any outstanding attentions and wait for the drive to
......@@ -522,115 +632,120 @@ get_data(unsigned char *data,
while (handle_sony_cd_attention())
;
}
if (is_busy())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
goto get_data_end;
}
else
{
/* Issue the command */
clear_result_ready();
clear_param_reg();
/* Issue the command */
clear_result_ready();
clear_param_reg();
write_params(params, 6);
write_cmd(SONY_READ_CMD);
write_params(params, 6);
write_cmd(SONY_READ_CMD);
/*
* Read the data from the drive one 2048 byte sector at a time. Handle
* any results received between sectors, if an error result is returned
* terminate the operation immediately.
*/
cur_offset = 0;
result_read = 0;
while (data_size > 0)
{
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready())))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!(is_result_ready() || is_data_ready()))
/*
* Read the data from the drive one 2048 byte sector at a time. Handle
* any results received between sectors, if an error result is returned
* terminate the operation immediately.
*/
cur_offset = 0;
result_read = 0;
while ((data_size > 0) && (result_buffer[0] == 0))
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
goto get_data_end;
}
/* Wait for the drive to tell us we have something */
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!(is_result_ready() || is_data_ready())))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!(is_result_ready() || is_data_ready()))
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
}
/* Handle results first */
if (is_result_ready())
{
result_read = 1;
get_result(result_buffer, result_size);
if ((*result_size < 2) || (result_buffer[0] != 0))
/* Handle results first */
else if (is_result_ready())
{
goto get_data_end;
result_read = 1;
get_result(result_buffer, result_size);
}
}
else /* Handle data next */
{
/*
* The drive has to be polled for status on a byte-by-byte basis
* to know if the data is ready. Yuck. I really wish I could use DMA.
*/
clear_data_ready();
for (i=0; i<2048; i++)
else /* Handle data next */
{
retry_count = jiffies + SONY_JIFFIES_TIMEOUT;
while ((retry_count > jiffies) && (!is_data_requested()))
{
while (handle_sony_cd_attention())
;
sony_sleep();
}
if (!is_data_requested())
{
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
goto get_data_end;
}
*data = read_data_register();
data++;
data_size--;
/*
* The drive has to be polled for status on a byte-by-byte basis
* to know if the data is ready. Yuck. I really wish I could use DMA.
*/
clear_data_ready();
read_data_block(data, result_buffer, result_size);
data += 2048;
data_size -= 2048;
cur_offset = cur_offset + 2048;
num_sectors_read++;
}
cur_offset = cur_offset + 2048;
}
}
/* Make sure the result has been read */
if (!result_read)
{
get_result(result_buffer, result_size);
/* Make sure the result has been read */
if (!result_read)
{
get_result(result_buffer, result_size);
}
}
get_data_end:
if ( ((result_buffer[0] & 0x20) == 0x20)
&& (result_buffer[1] != SONY_NOT_SPIN_ERR) /* No retry when not spin */
&& (num_retries < MAX_CDU31A_RETRIES))
{
/*
* If an error occurs, go back and only read one sector at the
* given location. Hopefully the error occurred on an unused
* sector after the first one. It is hard to say which sector
* the error occurred on because the drive returns status before
* the data transfer is finished and doesn't say which sector.
*/
data_size = 2048;
data = orig_data;
num_sectors_read = 0;
size_to_buf(1, &params[3]);
num_retries++;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 10; /* Wait .1 seconds on retries */
schedule();
/* Issue a reset on an error (the second time), othersize just delay */
if (num_retries == 2)
{
restart_on_error();
}
else
{
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + 10;
schedule();
}
/* Restart the operation. */
goto retry_data_operation;
}
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
return(num_sectors_read);
}
/*
* Do a command that does not involve data transfer.
* Do a command that does not involve data transfer. This routine must
* be re-entrant from the same task to support being called from the
* data operation code when an error occurs.
*/
static void
do_sony_cd_cmd(unsigned char cmd,
......@@ -641,6 +756,7 @@ do_sony_cd_cmd(unsigned char cmd,
{
unsigned int retry_count;
int num_retries;
int recursive_call;
cli();
......@@ -657,9 +773,14 @@ do_sony_cd_cmd(unsigned char cmd,
return;
}
}
sony_inuse = 1;
has_cd_task = current;
recursive_call = 0;
}
else
{
recursive_call = 1;
}
sony_inuse = 1;
has_cd_task = current;
sti();
num_retries = 0;
......@@ -681,18 +802,18 @@ do_sony_cd_cmd(unsigned char cmd,
result_buffer[0] = 0x20;
result_buffer[1] = SONY_TIMEOUT_OP_ERR;
*result_size = 2;
goto do_cmd_end;
}
else
{
clear_result_ready();
clear_param_reg();
clear_result_ready();
clear_param_reg();
write_params(params, num_params);
write_cmd(cmd);
write_params(params, num_params);
write_cmd(cmd);
get_result(result_buffer, result_size);
get_result(result_buffer, result_size);
}
do_cmd_end:
if ( ((result_buffer[0] & 0x20) == 0x20)
&& (num_retries < MAX_CDU31A_RETRIES))
{
......@@ -703,26 +824,41 @@ do_sony_cd_cmd(unsigned char cmd,
goto retry_cd_operation;
}
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
if (!recursive_call)
{
has_cd_task = NULL;
sony_inuse = 0;
wake_up_interruptible(&sony_wait);
}
}
/*
* Handle an attention from the drive. This will return 1 if it found one
* or 0 if not (if one is found, the caller might want to call again).
*
* This routine counts the number of consecutive times it is called
* (since this is always called from a while loop until it returns
* a 0), and returns a 0 if it happens too many times. This will help
* prevent a lockup.
*/
static int
handle_sony_cd_attention(void)
{
unsigned char atten_code;
unsigned char res_reg[2];
unsigned int res_size;
static int num_consecutive_attentions = 0;
if (is_attention())
{
if (num_consecutive_attentions > CDU31A_MAX_CONSECUTIVE_ATTENTIONS)
{
printk("cdu31a: Too many consecutive attentions: %d\n",
num_consecutive_attentions);
num_consecutive_attentions = 0;
return(0);
}
clear_attention();
atten_code = read_result_register();
......@@ -735,11 +871,6 @@ handle_sony_cd_attention(void)
sony_audio_status = CDROM_AUDIO_NO_STATUS;
sony_first_block = -1;
sony_last_block = -1;
if (initialized)
{
do_sony_cd_cmd(SONY_SPIN_UP_CMD, NULL, 0, res_reg, &res_size);
sony_get_toc();
}
break;
case SONY_AUDIO_PLAY_DONE_ATTN:
......@@ -758,9 +889,12 @@ handle_sony_cd_attention(void)
sony_audio_status = CDROM_AUDIO_ERROR;
break;
}
num_consecutive_attentions++;
return(1);
}
num_consecutive_attentions = 0;
return(0);
}
......@@ -862,6 +996,7 @@ do_cdu31a_request(void)
while (1)
{
cdu31a_request_startover:
/*
* The beginning here is stolen from the hard disk driver. I hope
* its right.
......@@ -878,7 +1013,7 @@ do_cdu31a_request(void)
if (dev != 0)
{
end_request(0);
continue;
goto cdu31a_request_startover;
}
switch(CURRENT->cmd)
......@@ -891,12 +1026,12 @@ do_cdu31a_request(void)
if ((block / 4) >= sony_toc->lead_out_start_lba)
{
end_request(0);
return;
goto cdu31a_request_startover;
}
if (((block + nsect) / 4) >= sony_toc->lead_out_start_lba)
{
end_request(0);
return;
goto cdu31a_request_startover;
}
while (nsect > 0)
......@@ -916,12 +1051,10 @@ do_cdu31a_request(void)
*/
if (((block / 4) + sony_buffer_sectors) >= sony_toc->lead_out_start_lba)
{
sony_last_block = (sony_toc->lead_out_start_lba * 4) - 1;
read_size = sony_toc->lead_out_start_lba - (block / 4);
}
else
{
sony_last_block = sony_first_block + (sony_buffer_sectors * 4) - 1;
read_size = sony_buffer_sectors;
}
size_to_buf(read_size, &params[3]);
......@@ -932,7 +1065,12 @@ do_cdu31a_request(void)
*/
spin_up_retry = 0;
try_read_again:
get_data(sony_buffer, params, (read_size * 2048), res_reg, &res_size);
sony_last_block = sony_first_block
+ (get_data(sony_buffer,
params,
(read_size * 2048),
res_reg,
&res_size) * 4) - 1;
if ((res_size < 2) || (res_reg[0] != 0))
{
if ((res_reg[1] == SONY_NOT_SPIN_ERR) && (!spin_up_retry))
......@@ -946,7 +1084,7 @@ do_cdu31a_request(void)
sony_first_block = -1;
sony_last_block = -1;
end_request(0);
return;
goto cdu31a_request_startover;
}
}
......@@ -1569,7 +1707,7 @@ static char *load_mech[] = { "caddy", "tray", "pop-up", "unknown" };
/* Read-ahead buffer sizes for different drives. These are just arbitrary
values, I don't know what is really optimum. */
static unsigned int mem_size[] = { 4096, 8192, 16384, 2048 };
static unsigned int mem_size[] = { 16384, 16384, 16384, 2048 };
void
get_drive_configuration(unsigned short base_io,
......@@ -1640,8 +1778,6 @@ unsigned long
cdu31a_init(unsigned long mem_start, unsigned long mem_end)
{
struct s_sony_drive_config drive_config;
unsigned char params[3];
unsigned char res_reg[2];
unsigned int res_size;
int i;
int drive_found;
......@@ -1695,17 +1831,7 @@ cdu31a_init(unsigned long mem_start, unsigned long mem_end)
}
printk("\n");
params[0] = SONY_SD_MECH_CONTROL;
params[1] = 0x03;
do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD,
params,
2,
res_reg,
&res_size);
if ((res_size < 2) || ((res_reg[0] & 0x20) == 0x20))
{
printk(" Unable to set mechanical parameters: 0x%2.2x\n", res_reg[1]);
}
set_drive_params();
blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */
......@@ -1716,8 +1842,6 @@ cdu31a_init(unsigned long mem_start, unsigned long mem_end)
mem_start += sizeof(*last_sony_subcode);
sony_buffer = (unsigned char *) mem_start;
mem_start += sony_buffer_size;
initialized = 1;
}
i++;
......
......@@ -48,7 +48,7 @@ OBJS := $(OBJS) msbusmouse.o
SRCS := $(SRCS) msbusmouse.c
endif
ifdef CONFIG_QUICKPORT_MOUSE
ifdef CONFIG_82C710_MOUSE
CONFIG_PSMOUSE = CONFIG_PSMOUSE
endif
......
......@@ -45,7 +45,7 @@ static int mouse_open(struct inode * inode, struct file * file)
file->f_op = &bus_mouse_fops;
break;
#endif
#if defined CONFIG_PSMOUSE || defined CONFIG_QUICKPORT_MOUSE
#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE
case PSMOUSE_MINOR:
file->f_op = &psaux_fops;
break;
......@@ -83,7 +83,7 @@ unsigned long mouse_init(unsigned long kmem_start)
#ifdef CONFIG_BUSMOUSE
kmem_start = bus_mouse_init(kmem_start);
#endif
#if defined CONFIG_PSMOUSE || defined CONFIG_QUICKPORT_MOUSE
#if defined CONFIG_PSMOUSE || defined CONFIG_82C710_MOUSE
kmem_start = psaux_init(kmem_start);
#endif
#ifdef CONFIG_MS_BUSMOUSE
......
......@@ -68,7 +68,7 @@
#define AUX_DISABLE_DEV 0xf5 /* disable aux device */
#define AUX_RESET 0xff /* reset aux device */
#define MAX_RETRIES 30 /* some aux operations take long time*/
#define MAX_RETRIES 60 /* some aux operations take long time*/
#define AUX_IRQ 12
#define AUX_BUF_SIZE 2048
......
......@@ -1574,7 +1574,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
*/
if (info->flags & ASYNC_CLOSING) {
interruptible_sleep_on(&info->close_wait);
return -EAGAIN;
return -ERESTARTSYS;
}
/*
......@@ -1632,7 +1632,7 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
current->state = TASK_INTERRUPTIBLE;
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
retval = -EAGAIN;
retval = -ERESTARTSYS;
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
......
......@@ -1315,6 +1315,7 @@ static int tty_open(struct inode * inode, struct file * filp)
int major, minor;
int noctty, retval;
retry_open:
minor = MINOR(inode->i_rdev);
major = MAJOR(inode->i_rdev);
noctty = filp->f_flags & O_NOCTTY;
......@@ -1372,7 +1373,12 @@ static int tty_open(struct inode * inode, struct file * filp)
#endif
release_dev(minor, filp);
return retval;
if (retval != -ERESTARTSYS)
return retval;
if (current->signal & ~current->blocked)
return retval;
schedule();
goto retry_open;
}
if (!noctty &&
current->leader &&
......
......@@ -881,6 +881,37 @@ slip_open(struct tty_struct *tty)
return(sl->line);
}
static struct enet_statistics *
sl_get_stats(struct device *dev)
{
static struct enet_statistics stats;
struct slip *sl;
struct slcompress *comp;
/* Find the correct SLIP channel to use. */
sl = &sl_ctrl[dev->base_addr];
if (! sl)
return NULL;
memset(&stats, 0, sizeof(struct enet_statistics));
stats.rx_packets = sl->rpacket;
stats.rx_over_errors = sl->roverrun;
stats.tx_packets = sl->spacket;
stats.tx_dropped = sl->sbusy;
stats.rx_errors = sl->errors;
comp = sl->slcomp;
if (comp) {
stats.rx_fifo_errors = comp->sls_i_compressed;
stats.rx_dropped = comp->sls_i_tossed;
stats.tx_fifo_errors = comp->sls_o_compressed;
stats.collisions = comp->sls_o_misses;
}
return (&stats);
}
/*
* Close down a SLIP channel.
......@@ -1193,6 +1224,7 @@ slip_init(struct device *dev)
dev->hard_header = sl_header;
dev->add_arp = sl_add_arp;
dev->type_trans = sl_type_trans;
dev->get_stats = sl_get_stats;
#ifdef HAVE_SET_MAC_ADDR
#ifdef CONFIG_AX25
dev->set_mac_address = sl_set_mac_address;
......
......@@ -38,6 +38,12 @@
* Al Longyear (longyear@sii.com)
* Removed erroneous code which mistakenly folded .data with .bss for
* a shared library.
*
* 8 Janurary 1994
* Al Longyear (longyear@sii.com)
* Corrected problem with read of library section returning the byte
* count rather than zero. This was a change between the pl12 and
* pl14 kernels which slipped by me.
*/
#include <linux/fs.h>
......@@ -650,12 +656,21 @@ preload_library (struct linux_binprm *exe_bprm,
buffer, /* Buffer for read */
nbytes); /* Byte count reqd. */
set_fs (old_fs); /* Restore the selector */
/*
* Check the result. The value returned is the byte count actaully read.
*/
if (status >= 0 && status != nbytes) {
#ifdef COFF_DEBUG
printk ("read of lib section was short\n");
#endif
status = -ENOEXEC;
}
}
/*
* At this point, go through the list of libraries in the data area.
*/
phdr = (COFF_SLIBHD *) buffer;
while (status == 0 && nbytes > COFF_SLIBSZ) {
while (status >= 0 && nbytes > COFF_SLIBSZ) {
int entry_size = COFF_LONG (phdr->sl_entsz) * sizeof (long);
int header_size = COFF_LONG (phdr->sl_pathndx) * sizeof (long);
/*
......
......@@ -4,8 +4,7 @@ Changes from version 0.4a to version 0.4b
- Clean up of balloc.c and ialloc.c.
- More consistency checks.
- Block preallocation added by Stephen Tweedie.
- Direct reads of directories disallowed if CONFIG_EXT2_FS_DIR_READ not
defined.
- Direct reads of directories disallowed.
- Readahead implemented in readdir by Stephen Tweedie.
- Bugs in block and inodes allocation fixed.
- Readahead implemented in ext2_find_entry by Chip Salzenberg.
......
......@@ -16,32 +16,23 @@
#include <asm/segment.h>
#include <linux/autoconf.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#include <linux/sched.h>
#include <linux/stat.h>
#ifndef CONFIG_EXT2_FS_DIR_READ
static int ext2_dir_read (struct inode * inode, struct file * filp,
char * buf, int count)
{
return -EISDIR;
}
#else
int ext2_file_read (struct inode *, struct file *, char *, int);
#endif
static int ext2_readdir (struct inode *, struct file *, struct dirent *, int);
static struct file_operations ext2_dir_operations = {
NULL, /* lseek - default */
#ifdef CONFIG_EXT2_FS_DIR_READ
ext2_file_read, /* read */
#else
ext2_dir_read, /* read */
#endif
NULL, /* write - bad */
ext2_readdir, /* readdir */
NULL, /* select - default */
......
......@@ -17,7 +17,6 @@
#include <asm/segment.h>
#include <asm/system.h>
#include <linux/autoconf.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/ext2_fs.h>
......@@ -34,10 +33,7 @@
#include <linux/fs.h>
#include <linux/ext2_fs.h>
#ifndef CONFIG_EXT2_FS_DIR_READ
static
#endif
int ext2_file_read (struct inode *, struct file *, char *, int);
static int ext2_file_read (struct inode *, struct file *, char *, int);
static int ext2_file_write (struct inode *, struct file *, char *, int);
static void ext2_release_file (struct inode *, struct file *);
......@@ -76,10 +72,7 @@ struct inode_operations ext2_file_inode_operations = {
ext2_permission /* permission */
};
#ifndef CONFIG_EXT2_FS_DIR_READ
static
#endif
int ext2_file_read (struct inode * inode, struct file * filp,
static int ext2_file_read (struct inode * inode, struct file * filp,
char * buf, int count)
{
int read, left, chars;
......@@ -97,7 +90,7 @@ int ext2_file_read (struct inode * inode, struct file * filp,
return -EINVAL;
}
sb = inode->i_sb;
if (!S_ISREG(inode->i_mode) && !S_ISDIR(inode->i_mode)) {
if (!S_ISREG(inode->i_mode)) {
ext2_warning (sb, "ext2_file_read", "mode = %07o",
inode->i_mode);
return -EINVAL;
......
......@@ -274,7 +274,7 @@ static void ext2_setup_super (struct super_block * sb,
printk ("EXT2-fs warning: mounting fs with errors, "
"running e2fsck is recommended\n");
else if (es->s_max_mnt_count >= 0 &&
es->s_mnt_count >= es->s_max_mnt_count)
es->s_mnt_count >= (unsigned short) es->s_max_mnt_count)
printk ("EXT2-fs warning: maximal mount count reached, "
"running e2fsck is recommended\n");
if (!(sb->s_flags & MS_RDONLY)) {
......
......@@ -4,16 +4,11 @@ It implements all of
- SystemV/386 FS,
- Coherent FS.
This is version alpha 5.
This is version beta 1.
To install:
* You need Linux 0.99.14.
* Go to /usr/src/linux, unpack the tar file there, and patch the Linux source:
patch -p1 < sysvfs.cdif
To build the Linux kernel with the patches:
make config
make depend
make
* Answer the 'System V and Coherent filesystem support' question with 'y'
when configuring the kernel.
* To mount a disk or a partition, use
mount [-r] -t sysv device mountpoint
The file system type names
......
......@@ -65,7 +65,8 @@ void sysv_free_inode(struct inode * inode)
return;
}
if (!(bh = sysv_bread(sb, inode->i_dev, sb->sv_firstinodezone + ((ino-1) >> sb->sv_inodes_per_block_bits), &bh_data))) {
panic("sysv_free_inode: unable to read inode block"); /* FIXME: too severe? */
printk("sysv_free_inode: unable to read inode block on device %d/%d\n",MAJOR(inode->i_dev),MINOR(inode->i_dev));
clear_inode(inode);
return;
}
raw_inode = (struct sysv_inode *) bh_data + ((ino-1) & sb->sv_inodes_per_block_1);
......
......@@ -316,7 +316,7 @@ struct super_block *sysv_read_super(struct super_block *sb,void *data,
sb->s_dev=0;
unlock_super(sb);
if (!silent)
printk("VFS: unable to read Xenix/SystemV/Coherent superblock\n");
printk("VFS: unable to read Xenix/SystemV/Coherent superblock on device %d/%d\n",MAJOR(dev),MINOR(dev));
return NULL;
ok:
......
......@@ -52,7 +52,7 @@
/*
* The second extended file system version
*/
#define EXT2FS_DATE "94/01/05"
#define EXT2FS_DATE "94/01/08"
#define EXT2FS_VERSION "0.4b"
/*
......
......@@ -31,7 +31,8 @@
#define ETH_P_PUP 0x0400 /* Xerox PUP packet */
#define ETH_P_IP 0x0800 /* Internet Protocol packet */
#define ETH_P_ARP 0x0806 /* Address Resolution packet */
#define ETH_P_RARP 0x0835 /* Reverse Addr Res packet */
#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
#define ETH_P_X25 0x0805 /* CCITT X.25 */
#define ETH_P_IPX 0x8137 /* IPX over DIX */
#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
......
......@@ -30,10 +30,14 @@ struct linger {
#define AF_UNSPEC 0
#define AF_UNIX 1
#define AF_INET 2
#define AF_AX25 3
#define AF_IPX 4
/* Protocol families, same as address families. */
#define PF_UNIX AF_UNIX
#define PF_INET AF_INET
#define PF_AX25 AF_AX25
#define PF_IPX AF_IPX
/* Flags we can use with send/ and recv. */
#define MSG_OOB 1
......@@ -41,6 +45,10 @@ struct linger {
/* Setsockoptions(2) level. */
#define SOL_SOCKET 1
#define SOL_IP 2
#define SOL_IPX 3
#define SOL_AX25 4
#define SOL_TCP 5
/* For setsockoptions(2) */
#define SO_DEBUG 1
......@@ -56,6 +64,19 @@ struct linger {
#define SO_NO_CHECK 11
#define SO_PRIORITY 12
#define SO_LINGER 13
/* IP options */
#define IP_TOS 1
#define IPTOS_LOWDELAY 0x10
#define IPTOS_THROUGHPUT 0x08
#define IPTOS_RELIABILITY 0x04
#define IP_TTL 2
/* IPX options */
#define IPX_TYPE 1
/* AX.25 options */
#define AX25_WINDOW 1
/* TCP options */
#define TCP_MSS 1
#define TCP_NODELAY 2
/* The various priorities. */
#define SOPRI_INTERACTIVE 0
......
......@@ -28,7 +28,7 @@ struct ip_config {
unsigned long paddr;
unsigned long router;
unsigned long net;
unsigned int up:1,destroy:1;
unsigned long up:1,destroy:1;
};
#endif /* FIXME: */
......
......@@ -235,7 +235,7 @@ static void calibrate_delay(void)
"r" (ticks),
"0" (loops_per_sec)
:"dx");
printk("ok - %lu.%02lu BogoMips (tm)\n",
printk("ok - %lu.%02lu BogoMips\n",
loops_per_sec/500000,
(loops_per_sec/5000) % 100);
return;
......
......@@ -36,6 +36,9 @@
* Alan Cox : skb->link3 maintained by letting the other xmit queue kill the packet.
* Alan Cox : Knows about type 3 devices (AX.25) using an AX.25 protocol ID not the ethernet
* one.
* Dominik Kubla : Better checking
* Tegge : Assorted corrections on cross port stuff
* Alan Cox : ATF_PERM was backwards! - might be useful now (sigh)
*
* To Fix:
* : arp response allocates an skbuff to send. However there is a perfectly
......@@ -386,7 +389,7 @@ static struct arp_table *arp_lookup_proxy(unsigned long paddr)
/* Delete an ARP mapping entry in the cache. */
void
arp_destroy(unsigned long paddr)
arp_destructor(unsigned long paddr, int force)
{
struct arp_table *apt;
struct arp_table **lapt;
......@@ -406,6 +409,8 @@ arp_destroy(unsigned long paddr)
lapt = &arp_tables[hash];
while ((apt = *lapt) != NULL) {
if (apt->ip == paddr) {
if((apt->flags&ATF_PERM) && !force)
return;
*lapt = apt->next;
if(apt->flags&ATF_PUBL)
arp_proxies--;
......@@ -418,6 +423,23 @@ arp_destroy(unsigned long paddr)
sti();
}
/*
* Kill an entry - eg for ioctl()
*/
void arp_destroy(unsigned long paddr)
{
arp_destructor(paddr,1);
}
/*
* Delete a possibly invalid entry (see timer.c)
*/
void arp_destroy_maybe(unsigned long paddr)
{
arp_destructor(paddr,0);
}
/* Create an ARP entry. The caller should check for duplicates! */
static struct arp_table *
......@@ -536,16 +558,16 @@ arp_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
return(0);
}
/*
* A broadcast arp, ignore it
*/
/*
* A broadcast arp, ignore it
*/
if((dst&0xFF)==0xFF)
if(chk_addr(dst)==IS_BROADCAST)
{
kfree_skb(skb, FREE_READ);
return 0;
}
memcpy(&dst, ptr + (arp->ar_hln * 2) + arp->ar_pln, arp->ar_pln);
if ((addr_hint=chk_addr(dst)) != IS_MYADDR && arp_proxies==0) {
DPRINTF((DBG_ARP, "ARP: request was not for me!\n"));
......@@ -654,8 +676,8 @@ arp_find(unsigned char *haddr, unsigned long paddr, struct device *dev,
* just pretend we did not find it, and then arp_send will
* verify the address for us.
*/
if ((!(apt->flags & ATF_PERM)) ||
(!before(apt->last_used, jiffies+ARP_TIMEOUT) && apt->hlen != 0)) {
if ((apt->flags & ATF_PERM) ||
(apt->last_used < jiffies+ARP_TIMEOUT && apt->hlen != 0)) {
apt->last_used = jiffies;
memcpy(haddr, apt->ha, dev->addr_len);
return(0);
......@@ -814,6 +836,11 @@ arp_req_set(struct arpreq *req)
htype = ARPHRD_ETHER;
hlen = ETH_ALEN;
break;
case ARPHRD_AX25:
htype = ARPHRD_AX25;
hlen = 7;
break;
default:
return(-EPFNOSUPPORT);
}
......
......@@ -59,5 +59,6 @@ extern void arp_add_broad(unsigned long addr, struct device *dev);
extern void arp_queue(struct sk_buff *skb);
extern int arp_get_info(char *buffer);
extern int arp_ioctl(unsigned int cmd, void *arg);
extern void arp_destroy_maybe(unsigned long paddr);
#endif /* _ARP_H */
......@@ -2,7 +2,7 @@
* SUCS NET2 Debugged.
*
* Generic datagram handling routines. These are generic for all protocols. Possibly a generic IP version on top
* of these would make sense. Not tonight however 8-).
* of these would make sense. Not tonight however 8-).
* This is used because UDP, RAW, PACKET and the to be released IPX layer all have identical select code and mostly
* identical recvfrom() code. So we share it here. The select was shared before but buried in udp.c so I moved it.
*
......@@ -13,6 +13,7 @@
* Alan Cox : Rewrote skb_read_datagram to avoid the skb_peek_copy stuff.
* Alan Cox : Added support for SOCK_SEQPACKET. IPX can no longer use the SO_TYPE hack but
* AX.25 now works right, and SPX is feasible.
* Alan Cox : Fixed write select of non IP protocol crash.
*/
#include <linux/config.h>
......@@ -35,7 +36,7 @@
#include "udp.h"
#include "skbuff.h"
#include "sock.h"
/*
* Get a datagram skbuff, understands the peeking, nonblocking wakeups and possible
......@@ -46,16 +47,16 @@
*/
struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock, int *err)
{
{
struct sk_buff *skb;
/* Socket is inuse - so the timer doesn't attack it */
restart:
sk->inuse = 1;
while(sk->rqueue == NULL) /* No data */
{
/* If we are shutdown then no more data is going to appear. We are done */
if (sk->shutdown & RCV_SHUTDOWN)
while(sk->rqueue == NULL) /* No data */
{
/* If we are shutdown then no more data is going to appear. We are done */
if (sk->shutdown & RCV_SHUTDOWN)
{
release_sock(sk);
*err=0;
......@@ -69,7 +70,7 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
sk->err=0;
return NULL;
}
/* Sequenced packets can come disconnected. If so we report the problem */
if(sk->type==SOCK_SEQPACKET && sk->state!=TCP_ESTABLISHED)
{
......@@ -77,24 +78,24 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
*err=-ENOTCONN;
return NULL;
}
/* User doesn't want to wait */
if (noblock)
if (noblock)
{
release_sock(sk);
*err=-EAGAIN;
return NULL;
}
release_sock(sk);
/* Interrupts off so that no packet arrives before we begin sleeping.
/* Interrupts off so that no packet arrives before we begin sleeping.
Otherwise we might miss our wake up */
cli();
if (sk->rqueue == NULL)
if (sk->rqueue == NULL)
{
interruptible_sleep_on(sk->sleep);
/* Signals may need a restart of the syscall */
if (current->signal & ~current->blocked)
if (current->signal & ~current->blocked)
{
sti();
*err=-ERESTARTSYS;
......@@ -115,26 +116,26 @@ struct sk_buff *skb_recv_datagram(struct sock *sk, unsigned flags, int noblock,
}
/* Again only user level code calls this function, so nothing interrupt level
will suddenely eat the rqueue */
if (!(flags & MSG_PEEK))
if (!(flags & MSG_PEEK))
{
skb=skb_dequeue(&sk->rqueue);
if(skb!=NULL)
skb->users++;
skb=skb_dequeue(&sk->rqueue);
if(skb!=NULL)
skb->users++;
else
goto restart; /* Avoid race if someone beats us to the data */
}
else
{
cli();
skb=skb_peek(&sk->rqueue);
if(skb!=NULL)
skb->users++;
sti();
if(skb==NULL) /* shouldn't happen but .. */
*err=-EAGAIN;
cli();
skb=skb_peek(&sk->rqueue);
if(skb!=NULL)
skb->users++;
sti();
if(skb==NULL) /* shouldn't happen but .. */
*err=-EAGAIN;
}
return skb;
}
}
void skb_free_datagram(struct sk_buff *skb)
{
......@@ -156,7 +157,7 @@ void skb_free_datagram(struct sk_buff *skb)
void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
{
/* We will know all about the fraglist options to allow >4K receives
/* We will know all about the fraglist options to allow >4K receives
but not this release */
memcpy_tofs(to,skb->h.raw+offset,size);
}
......@@ -165,11 +166,11 @@ void skb_copy_datagram(struct sk_buff *skb, int offset, char *to, int size)
* Datagram select: Again totally generic. Moved from udp.c
* Now does seqpacket.
*/
int datagram_select(struct sock *sk, int sel_type, select_table *wait)
{
select_wait(sk->sleep, wait);
switch(sel_type)
switch(sel_type)
{
case SEL_IN:
if (sk->type==SOCK_SEQPACKET && sk->state==TCP_CLOSE)
......@@ -177,7 +178,7 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait)
/* Connection closed: Wake up */
return(1);
}
if (sk->rqueue != NULL || sk->err != 0)
if (sk->rqueue != NULL || sk->err != 0)
{ /* This appears to be consistent
with other stacks */
return(1);
......@@ -185,16 +186,20 @@ int datagram_select(struct sock *sk, int sel_type, select_table *wait)
return(0);
case SEL_OUT:
if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE)
if (sk->prot->wspace(sk) >= MIN_WRITE_SPACE)
{
return(1);
}
if (sk->prot==NULL && sk->sndbuf-sk->wmem_alloc >= MIN_WRITE_SPACE)
{
return(1);
}
return(0);
case SEL_EX:
if (sk->err)
return(1); /* Socket has gone into error state (eg icmp error) */
return(0);
}
return(0);
}
return(0);
}
......@@ -26,6 +26,10 @@
* a) actually works for all A/B nets
* b) doesn't forward off the same interface.
* Alan Cox: Multiple extra protocols
* Alan Cox: Fixed ifconfig up of dud device setting the up flag
* Alan Cox: Fixed verify_area errors
* Alan Cox: Removed IP_SET_DEV as per Fred's comment. I hope this doesn't give
* anything away 8)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -709,9 +713,12 @@ dev_ifconf(char *arg)
struct device *dev;
char *pos;
int len;
int err;
/* Fetch the caller's info block. */
verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
if(err)
return err;
memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
len = ifc.ifc_len;
pos = ifc.ifc_buf;
......@@ -800,7 +807,9 @@ dev_ifsioc(void *arg, unsigned int getset)
int ret;
/* Fetch the caller's info block. */
verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
if(err)
return err;
memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
/* See which interface the caller is talking about. */
......@@ -827,8 +836,12 @@ dev_ifsioc(void *arg, unsigned int getset)
if ((old_flags & IFF_UP) && ((dev->flags & IFF_UP) == 0)) {
ret = dev_close(dev);
} else
{
ret = (! (old_flags & IFF_UP) && (dev->flags & IFF_UP))
? dev_open(dev) : 0;
if(ret<0)
dev->flags&=~IFF_UP; /* Didnt open so down the if */
}
}
break;
case SIOCGIFADDR:
......@@ -946,55 +959,9 @@ dev_ioctl(unsigned int cmd, void *arg)
int ret;
switch(cmd) {
case IP_SET_DEV:
{ /* Maintain backwards-compatibility, to be deleted for 1.00. */
struct device *dev;
/* The old 'struct ip_config'. */
struct ip_config {
char name[MAX_IP_NAME];
unsigned long paddr, router, net,up:1,destroy:1;
} ipc;
int retval, loopback;
printk("INET: Warning: old-style ioctl(IP_SET_DEV) called!\n");
if (!suser())
return (-EPERM);
verify_area (VERIFY_WRITE, arg, sizeof (ipc));
memcpy_fromfs(&ipc, arg, sizeof (ipc));
ipc.name[MAX_IP_NAME-1] = 0;
loopback = (strcmp(ipc.name, "loopback") == 0);
dev = dev_get( loopback ? "lo" : ipc.name);
if (dev == NULL)
return -EINVAL;
ipc.destroy = 0;
dev->pa_addr = ipc.paddr;
dev->family = AF_INET;
dev->pa_mask = get_mask(dev->pa_addr);
dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
if (ipc.net != 0xffffffff) {
dev->flags |= IFF_BROADCAST;
dev->pa_brdaddr = ipc.net;
}
/* To be proper we should delete the route here. */
if (ipc.up == 0)
return (dev->flags & IFF_UP != 0) ? dev_close(dev) : 0;
if ((dev->flags & IFF_UP) == 0
&& (retval = dev_open(dev)) != 0)
return retval;
printk("%s: adding HOST route of %8.8lx.\n", dev->name,
htonl(ipc.paddr));
rt_add(RTF_HOST, ipc.paddr, 0, 0, dev);
if (ipc.router != 0 && ipc.router != -1) {
rt_add(RTF_GATEWAY, ipc.paddr, 0, ipc.router, dev);
printk("%s: adding GATEWAY route of %8.8lx.\n",
dev->name, htonl(ipc.paddr));
}
return 0;
}
case IP_SET_DEV:
printk("Your network configuration program needs upgrading.\n");
return -EINVAL;
case SIOCGIFCONF:
(void) dev_ifconf((char *) arg);
ret = 0;
......
......@@ -17,6 +17,7 @@
* Alan Cox : eth_header ntohs should be htons
* Alan Cox : eth_rebuild_header missing an htons and
* minor other things.
* Tegge : Arp bug fixes.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -124,7 +125,7 @@ eth_header(unsigned char *buff, struct device *dev, unsigned short type,
cli();
memcpy(eth->h_source, &saddr, 4);
/* No. Ask ARP to resolve the Ethernet address. */
if (arp_find(eth->h_dest, daddr, dev, saddr))
if (arp_find(eth->h_dest, daddr, dev, dev->pa_addr))
{
sti();
if(type!=ETH_P_IP)
......@@ -155,7 +156,7 @@ eth_rebuild_header(void *buff, struct device *dev)
DPRINTF((DBG_DEV, "ETH: RebuildHeader: SRC=%s ", in_ntoa(src)));
DPRINTF((DBG_DEV, "DST=%s\n", in_ntoa(dst)));
if(eth->h_proto!=htons(ETH_P_ARP)) /* This ntohs kind of helps a bit! */
if (arp_find(eth->h_dest, dst, dev, src)) return(1);
if (arp_find(eth->h_dest, dst, dev, dev->pa_addr /* src */)) return(1);
memcpy(eth->h_source, dev->dev_addr, dev->addr_len);
return(0);
}
......
......@@ -14,6 +14,7 @@
* Fixes:
* Alan Cox : Generic queue usage.
* Gerhard Koerting: ICMP addressing corrected
* Alan Cox : Use tos/ttl settings
*
*
* This program is free software; you can redistribute it and/or
......@@ -108,7 +109,7 @@ icmp_send(struct sk_buff *skb_in, int type, int code, struct device *dev)
/* Build Layer 2-3 headers for message back to source. */
offset = ip_build_header(skb, dev->pa_addr, iph->saddr,
&dev, IPPROTO_ICMP, NULL, len);
&dev, IPPROTO_ICMP, NULL, len, skb_in->ip_hdr->tos,255);
if (offset < 0) {
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
......@@ -255,7 +256,7 @@ icmp_echo(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
/* Build Layer 2-3 headers for message back to source */
offset = ip_build_header(skb2, daddr, saddr, &dev,
IPPROTO_ICMP, opt, len);
IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
if (offset < 0) {
printk("ICMP: Could not build IP Header for ICMP ECHO Response\n");
kfree_skb(skb2,FREE_WRITE);
......@@ -319,7 +320,7 @@ icmp_address(struct icmphdr *icmph, struct sk_buff *skb, struct device *dev,
/* Build Layer 2-3 headers for message back to source */
offset = ip_build_header(skb2, daddr, saddr, &dev,
IPPROTO_ICMP, opt, len);
IPPROTO_ICMP, opt, len, skb->ip_hdr->tos,255);
if (offset < 0) {
printk("ICMP: Could not build IP Header for ICMP ADDRESS Response\n");
kfree_skb(skb2,FREE_WRITE);
......
......@@ -31,6 +31,9 @@
* Gerhard Koerting: IP interface addressing fix.
* Linus Torvalds : More robustness checks
* Alan Cox : Even more checks: Still not as robust as it ought to be
* Alan Cox : Save IP header pointer for later
* Alan Cox : ip option setting
* Alan Cox : Use ip_tos/ip_ttl settings
*
* To Fix:
* IP option processing is mostly not needed. ip_forward needs to know about routing rules
......@@ -198,7 +201,7 @@ ip_send(struct sk_buff *skb, unsigned long daddr, int len, struct device *dev,
*/
int
ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
struct device **dev, int type, struct options *opt, int len)
struct device **dev, int type, struct options *opt, int len, int tos, int ttl)
{
static struct options optmem;
struct iphdr *iph;
......@@ -256,9 +259,9 @@ ip_build_header(struct sk_buff *skb, unsigned long saddr, unsigned long daddr,
iph = (struct iphdr *)buff;
iph->version = 4;
iph->tos = 0;
iph->tos = tos;
iph->frag_off = 0;
iph->ttl = 32;
iph->ttl = ttl;
iph->daddr = daddr;
iph->saddr = saddr;
iph->protocol = type;
......@@ -1267,6 +1270,8 @@ ip_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt)
}
/* Point into the IP datagram, just past the header. */
skb->ip_hdr = iph;
skb->h.raw += iph->ihl*4;
hash = iph->protocol & (MAX_INET_PROTOS -1);
for (ipprot = (struct inet_protocol *)inet_protos[hash];
......@@ -1504,3 +1509,72 @@ int backoff(int n)
}
}
/*
* Socket option code for IP. This is the end of the line after any TCP,UDP etc options on
* an IP socket.
*/
int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
{
int val,err;
if (optval == NULL)
return(-EINVAL);
err=verify_area(VERIFY_READ, optval, sizeof(int));
if(err)
return err;
val = get_fs_long((unsigned long *)optval);
if(level!=SOL_IP)
return -EOPNOTSUPP;
switch(optname)
{
case IP_TOS:
if(val<0||val>255)
return -EINVAL;
sk->ip_tos=val;
return 0;
case IP_TTL:
if(val<1||val<255)
return -EINVAL;
sk->ip_ttl=val;
return 0;
/* IP_OPTIONS and friends go here eventually */
default:
return(-ENOPROTOOPT);
}
}
int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
{
int val,err;
if(level!=SOL_IP)
return -EOPNOTSUPP;
switch(optname)
{
case IP_TOS:
val=sk->ip_tos;
break;
case IP_TTL:
val=sk->ip_ttl;
break;
default:
return(-ENOPROTOOPT);
}
err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
if(err)
return err;
put_fs_long(sizeof(int),(unsigned long *) optlen);
err=verify_area(VERIFY_WRITE, optval, sizeof(int));
if(err)
return err;
put_fs_long(val,(unsigned long *)optval);
return(0);
}
......@@ -69,7 +69,8 @@ extern int ip_build_header(struct sk_buff *skb,
unsigned long saddr,
unsigned long daddr,
struct device **dev, int type,
struct options *opt, int len);
struct options *opt, int len,
int tos,int ttl);
extern unsigned short ip_compute_csum(unsigned char * buff, int len);
extern int ip_rcv(struct sk_buff *skb, struct device *dev,
struct packet_type *pt);
......@@ -77,5 +78,7 @@ extern void ip_queue_xmit(struct sock *sk,
struct device *dev, struct sk_buff *skb,
int free);
extern void ip_retransmit(struct sock *sk, int all);
extern int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
extern int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
#endif /* _IP_H */
......@@ -18,6 +18,7 @@
* added. Also fixed the peek/read crash
* from all old Linux datagram code.
* Alan Cox : Uses the improved datagram code.
* Alan Cox : Added NULL's for socket options.
*
*
* This program is free software; you can redistribute it and/or
......@@ -264,6 +265,8 @@ struct proto packet_prot = {
NULL,
packet_init,
NULL,
NULL, /* No set/get socket options */
NULL,
128,
0,
{NULL,},
......
......@@ -19,6 +19,10 @@
* Alan Cox : Checks sk->broadcast.
* Alan Cox : Uses skb_free_datagram/skb_copy_datagram
* Alan Cox : Raw passes ip options too
* Alan Cox : Setsocketopt added
* Alan Cox : Fixed error return for broadcasts
* Alan Cox : Removed wake_up calls
* Alan Cox : Use ttl/tos
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -76,7 +80,7 @@ raw_err (int err, unsigned char *header, unsigned long daddr,
}
sk->err = icmp_err_convert[err & 0xff].errno;
wake_up(sk->sleep);
sk->error_report(sk);
return;
}
......@@ -123,7 +127,7 @@ raw_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
}
sk->rmem_alloc += skb->mem_len;
skb_queue_tail(&sk->rqueue,skb);
wake_up(sk->sleep);
sk->data_ready(sk,skb->len);
release_sock(sk);
return(0);
}
......@@ -169,7 +173,7 @@ raw_sendto(struct sock *sk, unsigned char *from, int len,
if (sin.sin_port == 0) sin.sin_port = sk->protocol;
if (sk->broadcast == 0 && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
return -ENETUNREACH;
return -EACCES;
sk->inuse = 1;
skb = NULL;
......@@ -214,7 +218,7 @@ raw_sendto(struct sock *sk, unsigned char *from, int len,
tmp = sk->prot->build_header(skb, sk->saddr,
sin.sin_addr.s_addr, &dev,
sk->protocol, sk->opt, skb->mem_len);
sk->protocol, sk->opt, skb->mem_len, sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
DPRINTF((DBG_RAW, "raw_sendto: error building ip header.\n"));
kfree_skb(skb,FREE_WRITE);
......@@ -330,6 +334,13 @@ raw_recvfrom(struct sock *sk, unsigned char *to, int len,
return err;
put_fs_long(sizeof(*sin), addr_len);
}
if(sin)
{
err=verify_area(VERIFY_WRITE, sin, sizeof(*sin));
if(err)
return err;
}
err=verify_area(VERIFY_WRITE,to,len);
if(err)
return err;
......@@ -348,7 +359,6 @@ raw_recvfrom(struct sock *sk, unsigned char *to, int len,
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = skb->daddr;
verify_area(VERIFY_WRITE, sin, sizeof(*sin));
memcpy_tofs(sin, &addr, sizeof(*sin));
}
......@@ -390,6 +400,8 @@ struct proto raw_prot = {
NULL,
raw_init,
NULL,
ip_setsockopt,
ip_getsockopt,
128,
0,
{NULL,},
......
......@@ -121,21 +121,27 @@ void rt_flush(struct device *dev)
* number of zero 8-bit net numbers, otherwise we use the "default"
* masks judging by the destination address and our device netmask.
*/
static inline unsigned long default_mask(unsigned long dst)
{
dst = ntohl(dst);
if (IN_CLASSA(dst))
return htonl(IN_CLASSA_NET);
if (IN_CLASSB(dst))
return htonl(IN_CLASSB_NET);
return htonl(IN_CLASSC_NET);
}
static unsigned long guess_mask(unsigned long dst, struct device * dev)
{
unsigned long mask = 0xffffffff;
/* this is a rather ugly optimization: works only on little-endian machines */
while (mask & dst)
mask >>= 8;
mask <<= 8;
if (mask)
return ~mask;
dst = ntohl(dst);
if (IN_CLASSA(dst))
mask = htonl(IN_CLASSA_NET);
else if (IN_CLASSB(dst))
mask = htonl(IN_CLASSB_NET);
else
mask = htonl(IN_CLASSC_NET);
/* ok, no more hacks.. */
mask = default_mask(dst);
if (dev->flags & IFF_POINTOPOINT)
return mask;
if ((dst ^ dev->pa_addr) & mask)
......
......@@ -13,7 +13,7 @@
* and memory leak hunting.
* Alan Cox : More generic kfree handler
*/
#include <linux/config.h>
#include <linux/types.h>
#include <linux/kernel.h>
......@@ -44,14 +44,14 @@
/*
* Resource tracking variables
*/
volatile unsigned long net_memory=0;
volatile unsigned long net_skbcount=0;
/*
* Debugging paranoia. Can go later when this crud stack works
*/
*/
void skb_check(struct sk_buff *skb, int line, char *file)
......@@ -81,20 +81,20 @@ void skb_check(struct sk_buff *skb, int line, char *file)
/*
* Insert an sk_buff at the start of a list.
*/
void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk)
{
unsigned long flags;
IS_SKB(newsk);
IS_SKB(newsk);
if(newsk->list)
printk("Suspicious queue head: sk_buff on list!\n");
save_flags(flags);
cli();
newsk->list=list;
newsk->next=*list;
if(*list)
newsk->prev=(*list)->prev;
else
......@@ -110,14 +110,14 @@ void skb_queue_head(struct sk_buff *volatile* list,struct sk_buff *newsk)
/*
* Insert an sk_buff at the end of a list.
*/
void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk)
{
unsigned long flags;
if(newsk->list)
printk("Suspicious queue tail: sk_buff on list!\n");
IS_SKB(newsk);
save_flags(flags);
cli();
......@@ -137,7 +137,7 @@ void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk)
*list=newsk;
}
IS_SKB(newsk->prev);
IS_SKB(newsk->next);
IS_SKB(newsk->next);
restore_flags(flags);
}
......@@ -146,21 +146,21 @@ void skb_queue_tail(struct sk_buff *volatile* list, struct sk_buff *newsk)
* Remove an sk_buff from a list. This routine is also interrupt safe
* so you can grab read and free buffers as another process adds them.
*/
struct sk_buff *skb_dequeue(struct sk_buff *volatile* list)
{
long flags;
struct sk_buff *result;
save_flags(flags);
cli();
if(*list==NULL)
{
restore_flags(flags);
return(NULL);
}
result=*list;
if(result->next==result)
*list=NULL;
......@@ -173,7 +173,7 @@ struct sk_buff *skb_dequeue(struct sk_buff *volatile* list)
IS_SKB(result);
restore_flags(flags);
if(result->list!=list)
printk("Dequeued packet has invalid list pointer\n");
......@@ -186,19 +186,19 @@ struct sk_buff *skb_dequeue(struct sk_buff *volatile* list)
/*
* Insert a packet before another one in a list.
*/
void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
IS_SKB(old);
IS_SKB(newsk);
if(!old->list)
printk("insert before unlisted item!\n");
if(newsk->list)
printk("inserted item is already on a list.\n");
save_flags(flags);
cli();
newsk->list=old->list;
......@@ -206,18 +206,18 @@ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
newsk->prev=old->prev;
newsk->next->prev=newsk;
newsk->prev->next=newsk;
restore_flags(flags);
}
/*
* Place a packet after a given packet in a list.
*/
void skb_append(struct sk_buff *old, struct sk_buff *newsk)
{
unsigned long flags;
IS_SKB(old);
IS_SKB(newsk);
......@@ -225,7 +225,7 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
printk("append before unlisted item!\n");
if(newsk->list)
printk("append item is already on a list.\n");
save_flags(flags);
cli();
newsk->list=old->list;
......@@ -233,7 +233,7 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
newsk->next=old->next;
newsk->next->prev=newsk;
newsk->prev->next=newsk;
restore_flags(flags);
}
......@@ -243,7 +243,7 @@ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
* MUST EXIST when you unlink. Thus a list must have its contents unlinked
* _FIRST_.
*/
void skb_unlink(struct sk_buff *skb)
{
unsigned long flags;
......@@ -251,7 +251,7 @@ void skb_unlink(struct sk_buff *skb)
cli();
IS_SKB(skb);
if(skb->list)
{
skb->next->prev=skb->prev;
......@@ -290,7 +290,7 @@ void skb_new_list_head(struct sk_buff *volatile* list)
while(skb!=*list);
}
}
/*
* Peek an sk_buff. Unlike most other operations you _MUST_
* be careful with this one. A peek leaves the buffer on the
......@@ -310,14 +310,14 @@ struct sk_buff *skb_peek(struct sk_buff *volatile* list)
* anyway. Only the memcpy of upto 4K with ints off is not
* as nice as I'd like.
*/
struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
{
struct sk_buff *orig,*newsk;
unsigned long flags;
unsigned int len;
/* Now for some games to avoid races */
do
{
save_flags(flags);
......@@ -336,7 +336,7 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
if(newsk==NULL) /* Oh dear... not to worry */
return NULL;
save_flags(flags);
cli();
if(skb_peek(list)!=orig) /* List changed go around another time */
......@@ -349,7 +349,7 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
kfree_skb(newsk, FREE_WRITE);
continue;
}
IS_SKB(orig);
IS_SKB(newsk);
memcpy(newsk,orig,len);
......@@ -364,11 +364,11 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
newsk->free=1;
}
while(0);
restore_flags(flags);
return(newsk);
}
}
/*
* Free an sk_buff. This still knows about things it should
* not need to like protocols and sockets.
......@@ -376,68 +376,68 @@ struct sk_buff *skb_peek_copy(struct sk_buff *volatile* list)
void kfree_skb(struct sk_buff *skb, int rw)
{
if (skb == NULL) {
printk("kfree_skb: skb = NULL\n");
return;
}
IS_SKB(skb);
if(skb->free == 2)
printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n");
if(skb->list)
printk("Warning: kfree_skb passed an skb still on a list.\n");
skb->magic = 0;
if (skb->sk)
{
if(skb->sk->prot!=NULL)
{
if (rw)
skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len);
else
skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len);
if (skb == NULL) {
printk("kfree_skb: skb = NULL\n");
return;
}
else
IS_SKB(skb);
if(skb->free == 2)
printk("Warning: kfree_skb passed an skb that nobody set the free flag on!\n");
if(skb->list)
printk("Warning: kfree_skb passed an skb still on a list.\n");
skb->magic = 0;
if (skb->sk)
{
if(skb->sk->prot!=NULL)
{
/* Non INET - default wmalloc/rmalloc handler */
if (rw)
skb->sk->rmem_alloc-=skb->mem_len;
if (rw)
skb->sk->prot->rfree(skb->sk, skb->mem_addr, skb->mem_len);
else
skb->sk->prot->wfree(skb->sk, skb->mem_addr, skb->mem_len);
}
else
skb->sk->wmem_alloc-=skb->mem_len;
if(!skb->sk->dead)
{
/* Non INET - default wmalloc/rmalloc handler */
if (rw)
skb->sk->rmem_alloc-=skb->mem_len;
else
skb->sk->wmem_alloc-=skb->mem_len;
if(!skb->sk->dead)
wake_up(skb->sk->sleep);
kfree_skbmem(skb->mem_addr,skb->mem_len);
kfree_skbmem(skb->mem_addr,skb->mem_len);
}
}
}
else
kfree_skbmem(skb->mem_addr, skb->mem_len);
else
kfree_skbmem(skb->mem_addr, skb->mem_len);
}
/*
* Allocate a new skbuff. We do this ourselves so we can fill in a few 'private'
* fields and also do memory statistics to find all the [BEEP] leaks.
*/
struct sk_buff *alloc_skb(unsigned int size,int priority)
{
struct sk_buff *skb=(struct sk_buff *)kmalloc(size,priority);
if(skb==NULL)
return NULL;
skb->free= 2; /* Invalid so we pick up forgetful users */
skb->list= 0; /* Not on a list */
skb->truesize=size;
skb->mem_len=size;
skb->mem_addr=skb;
skb->fraglist=NULL;
net_memory+=size;
net_skbcount++;
skb->magic_debug_cookie=SK_GOOD_SKB;
skb->users=0;
return skb;
struct sk_buff *skb=(struct sk_buff *)kmalloc(size,priority);
if(skb==NULL)
return NULL;
skb->free= 2; /* Invalid so we pick up forgetful users */
skb->list= 0; /* Not on a list */
skb->truesize=size;
skb->mem_len=size;
skb->mem_addr=skb;
skb->fraglist=NULL;
net_memory+=size;
net_skbcount++;
skb->magic_debug_cookie=SK_GOOD_SKB;
skb->users=0;
return skb;
}
/*
* Free an skbuff by memory
*/
*/
void kfree_skbmem(void *mem,unsigned size)
{
......@@ -451,4 +451,4 @@ void kfree_skbmem(void *mem,unsigned size)
net_memory-=size;
}
}
......@@ -60,6 +60,7 @@ struct sk_buff {
ipx_packet *ipx;
#endif
} h;
struct iphdr *ip_hdr; /* For IPPROTO_RAW */
unsigned long mem_len;
unsigned long len;
unsigned long fraglen;
......
......@@ -48,6 +48,9 @@
* Alan Cox : SO_LINGER supported
* Alan Cox : Error reporting fixes
* Anonymous : inet_create tidied up (sk->reuse setting)
* Alan Cox : inet sockets don't set sk->type!
* Alan Cox : Split socket option code
* Alan Cox : Callbacks
*
* To Fix:
*
......@@ -312,7 +315,7 @@ destroy_sock(struct sock *sk)
/* Incase it's sleeping somewhere. */
if (!sk->dead)
wake_up(sk->sleep);
sk->write_space(sk);
remove_sock(sk);
......@@ -471,205 +474,236 @@ inet_fcntl(struct socket *sock, unsigned int cmd, unsigned long arg)
}
}
static int
inet_setsockopt(struct socket *sock, int level, int optname,
/*
* Set socket options on an inet socket.
*/
static int inet_setsockopt(struct socket *sock, int level, int optname,
char *optval, int optlen)
{
struct sock *sk;
int val;
int err;
struct linger ling;
/* This should really pass things on to the other levels. */
if (level != SOL_SOCKET) return(-EOPNOTSUPP);
struct sock *sk = (struct sock *) sock->data;
if (level == SOL_SOCKET)
return sock_setsockopt(sk,level,optname,optval,optlen);
if (sk->prot->setsockopt==NULL)
return(-EOPNOTSUPP);
else
return sk->prot->setsockopt(sk,level,optname,optval,optlen);
}
sk = (struct sock *) sock->data;
if (sk == NULL) {
printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
return(0);
}
if (optval == NULL) return(-EINVAL);
err=verify_area(VERIFY_READ, optval, sizeof(int));
if(err)
return err;
val = get_fs_long((unsigned long *)optval);
switch(optname) {
case SO_TYPE:
case SO_ERROR:
return(-ENOPROTOOPT);
case SO_DEBUG:
sk->debug=val?1:0;
case SO_DONTROUTE: /* Still to be implemented */
return(0);
case SO_BROADCAST:
sk->broadcast=val?1:0;
return 0;
case SO_SNDBUF:
if(val>32767)
val=32767;
if(val<256)
val=256;
sk->sndbuf=val;
return 0;
case SO_LINGER:
err=verify_area(VERIFY_READ,optval,sizeof(ling));
if(err)
return err;
memcpy_fromfs(&ling,optval,sizeof(ling));
if(ling.l_onoff==0)
sk->linger=0;
else
{
sk->lingertime=ling.l_linger;
sk->linger=1;
}
return 0;
case SO_RCVBUF:
if(val>32767)
val=32767;
if(val<256)
val=256;
sk->rcvbuf=val;
return(0);
case SO_REUSEADDR:
if (val) sk->reuse = 1;
else sk->reuse = 0;
return(0);
case SO_KEEPALIVE:
if (val) sk->keepopen = 1;
else sk->keepopen = 0;
return(0);
static int inet_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk = sock->data;
if (level == SOL_SOCKET)
return sock_getsockopt(sk,level,optname,optval,optlen);
if(sk->prot->getsockopt==NULL)
return(-EOPNOTSUPP);
else
return sk->prot->getsockopt(sk,level,optname,optval,optlen);
}
case SO_OOBINLINE:
if (val) sk->urginline = 1;
else sk->urginline = 0;
return(0);
/*
* This is meant for all protocols to use and covers goings on
* at the socket level. Everything here is generic.
*/
case SO_NO_CHECK:
if (val) sk->no_check = 1;
else sk->no_check = 0;
return(0);
int sock_setsockopt(struct sock *sk, int level, int optname,
char *optval, int optlen)
{
int val;
int err;
struct linger ling;
case SO_PRIORITY:
if (val >= 0 && val < DEV_NUMBUFFS) {
sk->priority = val;
} else {
return(-EINVAL);
}
return(0);
if (optval == NULL)
return(-EINVAL);
default:
return(-ENOPROTOOPT);
}
err=verify_area(VERIFY_READ, optval, sizeof(int));
if(err)
return err;
val = get_fs_long((unsigned long *)optval);
switch(optname)
{
case SO_TYPE:
case SO_ERROR:
return(-ENOPROTOOPT);
case SO_DEBUG:
sk->debug=val?1:0;
case SO_DONTROUTE: /* Still to be implemented */
return(0);
case SO_BROADCAST:
sk->broadcast=val?1:0;
return 0;
case SO_SNDBUF:
if(val>32767)
val=32767;
if(val<256)
val=256;
sk->sndbuf=val;
return 0;
case SO_LINGER:
err=verify_area(VERIFY_READ,optval,sizeof(ling));
if(err)
return err;
memcpy_fromfs(&ling,optval,sizeof(ling));
if(ling.l_onoff==0)
sk->linger=0;
else
{
sk->lingertime=ling.l_linger;
sk->linger=1;
}
return 0;
case SO_RCVBUF:
if(val>32767)
val=32767;
if(val<256)
val=256;
sk->rcvbuf=val;
return(0);
case SO_REUSEADDR:
if (val)
sk->reuse = 1;
else
sk->reuse = 0;
return(0);
case SO_KEEPALIVE:
if (val)
sk->keepopen = 1;
else
sk->keepopen = 0;
return(0);
case SO_OOBINLINE:
if (val)
sk->urginline = 1;
else
sk->urginline = 0;
return(0);
case SO_NO_CHECK:
if (val)
sk->no_check = 1;
else
sk->no_check = 0;
return(0);
case SO_PRIORITY:
if (val >= 0 && val < DEV_NUMBUFFS)
{
sk->priority = val;
}
else
{
return(-EINVAL);
}
return(0);
default:
return(-ENOPROTOOPT);
}
}
static int
inet_getsockopt(struct socket *sock, int level, int optname,
char *optval, int *optlen)
{
struct sock *sk;
int val;
int err;
struct linger ling;
/* This should really pass things on to the other levels. */
if (level != SOL_SOCKET) return(-EOPNOTSUPP);
int sock_getsockopt(struct sock *sk, int level, int optname,
char *optval, int *optlen)
{
int val;
int err;
struct linger ling;
sk = (struct sock *) sock->data;
if (sk == NULL) {
printk("Warning: sock->data = NULL: %d\n" ,__LINE__);
return(0);
}
switch(optname) {
case SO_DEBUG:
val = sk->debug;
break;
case SO_DONTROUTE: /* One last option to implement */
val = 0;
break;
switch(optname)
{
case SO_DEBUG:
val = sk->debug;
break;
case SO_BROADCAST:
val= sk->broadcast;
break;
case SO_DONTROUTE: /* One last option to implement */
val = 0;
break;
case SO_LINGER:
case SO_BROADCAST:
val= sk->broadcast;
break;
err=verify_area(VERIFY_WRITE,optval,sizeof(ling));
if(err)
return err;
err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
if(err)
return err;
put_fs_long(sizeof(ling),(unsigned long *)optlen);
ling.l_onoff=sk->linger;
ling.l_linger=sk->lingertime;
memcpy_tofs(optval,&ling,sizeof(ling));
return 0;
case SO_LINGER:
err=verify_area(VERIFY_WRITE,optval,sizeof(ling));
if(err)
return err;
err=verify_area(VERIFY_WRITE,optlen,sizeof(int));
if(err)
return err;
put_fs_long(sizeof(ling),(unsigned long *)optlen);
ling.l_onoff=sk->linger;
ling.l_linger=sk->lingertime;
memcpy_tofs(optval,&ling,sizeof(ling));
return 0;
case SO_SNDBUF:
val=sk->sndbuf;
break;
case SO_SNDBUF:
val=sk->sndbuf;
break;
case SO_RCVBUF:
val =sk->rcvbuf;
break;
case SO_REUSEADDR:
val = sk->reuse;
break;
case SO_RCVBUF:
val =sk->rcvbuf;
break;
case SO_KEEPALIVE:
val = sk->keepopen;
break;
case SO_REUSEADDR:
val = sk->reuse;
break;
case SO_TYPE:
if (sk->prot == &tcp_prot) val = SOCK_STREAM;
else val = SOCK_DGRAM;
break;
case SO_KEEPALIVE:
val = sk->keepopen;
break;
case SO_ERROR:
val = sk->err;
sk->err = 0;
break;
case SO_TYPE:
if (sk->prot == &tcp_prot)
val = SOCK_STREAM;
else
val = SOCK_DGRAM;
break;
case SO_OOBINLINE:
val = sk->urginline;
break;
case SO_ERROR:
val = sk->err;
sk->err = 0;
break;
case SO_NO_CHECK:
val = sk->no_check;
break;
case SO_OOBINLINE:
val = sk->urginline;
break;
case SO_NO_CHECK:
val = sk->no_check;
break;
case SO_PRIORITY:
val = sk->priority;
break;
case SO_PRIORITY:
val = sk->priority;
break;
default:
return(-ENOPROTOOPT);
}
err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
if(err)
return err;
put_fs_long(sizeof(int),(unsigned long *) optlen);
default:
return(-ENOPROTOOPT);
}
err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
if(err)
return err;
put_fs_long(sizeof(int),(unsigned long *) optlen);
err=verify_area(VERIFY_WRITE, optval, sizeof(int));
if(err)
return err;
put_fs_long(val,(unsigned long *)optval);
err=verify_area(VERIFY_WRITE, optval, sizeof(int));
if(err)
return err;
put_fs_long(val,(unsigned long *)optval);
return(0);
return(0);
}
static int
inet_listen(struct socket *sock, int backlog)
{
......@@ -698,6 +732,23 @@ inet_listen(struct socket *sock, int backlog)
return(0);
}
/*
* Default callbacks for user INET sockets. These just wake up
* the user owning the socket.
*/
static void def_callback1(struct sock *sk)
{
if(!sk->dead)
wake_up(sk->sleep);
}
static void def_callback2(struct sock *sk,int len)
{
if(!sk->dead)
wake_up(sk->sleep);
}
static int
inet_create(struct socket *sock, int protocol)
......@@ -773,6 +824,7 @@ inet_create(struct socket *sock, int protocol)
return(-ESOCKTNOSUPPORT);
}
sk->socket = sock;
sk->type = sock->type;
sk->protocol = protocol;
sk->wmem_alloc = 0;
sk->rmem_alloc = 0;
......@@ -853,6 +905,14 @@ inet_create(struct socket *sock, int protocol)
sk->dummy_th.urg = 0;
sk->dummy_th.dest = 0;
sk->ip_tos=0;
sk->ip_ttl=64;
sk->state_change = def_callback1;
sk->data_ready = def_callback2;
sk->write_space = def_callback1;
sk->error_report = def_callback1;
if (sk->num) {
/*
* It assumes that any protocol which allows
......@@ -893,7 +953,7 @@ inet_release(struct socket *sock, struct socket *peer)
if (sk == NULL) return(0);
DPRINTF((DBG_INET, "inet_release(sock = %X, peer = %X)\n", sock, peer));
wake_up(sk->sleep);
sk->state_change(sk);
/* Start closing the connection. This may take a while. */
/*
......@@ -1579,7 +1639,7 @@ sock_wfree(struct sock *sk, void *mem, unsigned long size)
sk->wmem_alloc -= size;
/* In case it might be waiting for more memory. */
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->write_space(sk);
if (sk->destroy && sk->wmem_alloc == 0 && sk->rmem_alloc == 0) {
DPRINTF((DBG_INET,
"recovered lost memory, sock = %X\n", sk));
......
......@@ -136,7 +136,11 @@ struct sock {
char ax25_retxqi;
char ax25_rrtimer;
char ax25_timer;
ax25_digi *ax25_digipeat;
#endif
/* IP 'private area' or will be eventually */
int ip_ttl; /* TTL setting */
int ip_tos; /* TOS */
struct tcphdr dummy_th;
/* This part is used for the timeout functions (timer.c). */
......@@ -145,6 +149,13 @@ struct sock {
/* identd */
struct socket *socket;
/* Callbacks */
void (*state_change)(struct sock *sk);
void (*data_ready)(struct sock *sk,int bytes);
void (*write_space)(struct sock *sk);
void (*error_report)(struct sock *sk);
};
struct proto {
......@@ -177,7 +188,7 @@ struct proto {
unsigned long saddr,
unsigned long daddr,
struct device **dev, int type,
struct options *opt, int len);
struct options *opt, int len, int tos, int ttl);
int (*connect)(struct sock *sk,
struct sockaddr_in *usin, int addr_len);
struct sock *(*accept) (struct sock *sk, int flags);
......@@ -197,6 +208,10 @@ struct proto {
unsigned long arg);
int (*init)(struct sock *sk);
void (*shutdown)(struct sock *sk, int how);
int (*setsockopt)(struct sock *sk, int level, int optname,
char *optval, int optlen);
int (*getsockopt)(struct sock *sk, int level, int optname,
char *optval, int *option);
unsigned short max_header;
unsigned long retransmits;
struct sock *sock_array[SOCK_ARRAY_SIZE];
......@@ -238,6 +253,8 @@ extern void sock_rfree(struct sock *sk, void *mem,
extern unsigned long sock_rspace(struct sock *sk);
extern unsigned long sock_wspace(struct sock *sk);
extern int sock_setsockopt(struct sock *sk,int level,int op,char *optval,int optlen);
extern int sock_getsockopt(struct sock *sk,int level,int op,char *optval,int *optlen);
/* declarations from timer.c */
extern struct sock *timer_base;
......
......@@ -47,6 +47,13 @@
* Michael O'Reilly : ack < copied bug fix.
* Johannes Stille : Misc tcp fixes (not all in yet).
* Alan Cox : FIN with no memory -> CRASH
* Alan Cox : Added socket option proto entries. Also added awareness of them to accept.
* Alan Cox : Added TCP options (SOL_TCP)
* Alan Cox : Switched wakeup calls to callbacks, so the kernel can layer network sockets.
* Alan Cox : Use ip_tos/ip_ttl settings.
* Alan Cox : Handle FIN (more) properly (we hope).
* Alan Cox : RST frames sent on unsynchronised state ack error/
* Alan Cox : Put in missing check for SYN bit.
*
*
* To Fix:
......@@ -155,7 +162,7 @@ static void tcp_time_wait(struct sock *sk)
sk->state = TCP_TIME_WAIT;
sk->shutdown = SHUTDOWN_MASK;
if (!sk->dead)
wake_up(sk->sleep);
sk->state_change(sk);
reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
}
......@@ -213,7 +220,7 @@ tcp_err(int err, unsigned char *header, unsigned long daddr,
if(err<0)
{
sk->err = -err;
wake_up(sk->sleep);
sk->error_report(sk);
return;
}
......@@ -237,7 +244,7 @@ tcp_err(int err, unsigned char *header, unsigned long daddr,
if (icmp_err_convert[err & 0xff].fatal) {
if (sk->state == TCP_SYN_SENT) {
sk->state = TCP_CLOSE;
wake_up(sk->sleep); /* Wake people up to see the error (see connect in sock.c) */
sk->error_report(sk); /* Wake people up to see the error (see connect in sock.c) */
}
}
return;
......@@ -558,7 +565,20 @@ tcp_send_partial(struct sock *sk)
if (sk == NULL || sk->send_tmp == NULL) return;
skb = sk->send_tmp;
/* If we have queued a header size packet.. */
if(skb->len-(unsigned long)skb->h.th + (unsigned long)(skb+1)==sizeof(struct tcphdr))
{
/* If its got a syn or fin its notionally included in the size..*/
if(!skb->h.th->syn && !skb->h.th->fin)
{
printk("tcp_send_partial: attempt to queue a bogon.\n");
kfree_skb(skb,FREE_WRITE);
sk->send_tmp=NULL;
return;
}
}
/* We need to complete and send the packet. */
tcp_send_check(skb->h.th, sk->saddr, sk->daddr,
skb->len-(unsigned long)skb->h.th +
......@@ -622,7 +642,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_ack: malloc failed\n");
/* Put in the IP header and routing stuff. */
tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,
IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
buff->free=1;
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
......@@ -887,7 +907,7 @@ tcp_write(struct sock *sk, unsigned char *from,
* Perhaps some hints here would be good.
*/
tmp = prot->build_header(skb, sk->saddr, sk->daddr, &dev,
IPPROTO_TCP, sk->opt, skb->mem_len);
IPPROTO_TCP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
if (tmp < 0 ) {
prot->wfree(sk, skb->mem_addr, skb->mem_len);
release_sock(sk);
......@@ -1008,7 +1028,7 @@ tcp_read_wakeup(struct sock *sk)
/* Put in the IP header and routing stuff. */
tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
buff->free=1;
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
......@@ -1484,7 +1504,7 @@ tcp_shutdown(struct sock *sk, int how)
/* Put in the IP header and routing stuff. */
tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
IPPROTO_TCP, sk->opt,
sizeof(struct tcphdr));
sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
buff->free=1;
prot->wfree(sk,buff->mem_addr, buff->mem_len);
......@@ -1569,7 +1589,7 @@ tcp_recvfrom(struct sock *sk, unsigned char *to,
/* This routine will send an RST to the other tcp. */
static void
tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
struct proto *prot, struct options *opt, struct device *dev)
struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl)
{
struct sk_buff *buff;
struct tcphdr *t1;
......@@ -1594,7 +1614,7 @@ tcp_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
/* Put in the IP header and routing stuff. */
tmp = prot->build_header(buff, saddr, daddr, &dev, IPPROTO_TCP, opt,
sizeof(struct tcphdr));
sizeof(struct tcphdr),tos,ttl);
if (tmp < 0) {
buff->free = 1;
prot->wfree(NULL, buff->mem_addr, buff->mem_len);
......@@ -1714,10 +1734,10 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
/* If the socket is dead, don't accept the connection. */
if (!sk->dead) {
wake_up(sk->sleep);
sk->data_ready(sk,0);
} else {
DPRINTF((DBG_TCP, "tcp_conn_request on dead socket\n"));
tcp_reset(daddr, saddr, th, sk->prot, opt, dev);
tcp_reset(daddr, saddr, th, sk->prot, opt, dev, sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
return;
}
......@@ -1802,21 +1822,12 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
newsk->acked_seq = skb->h.th->seq + 1;
newsk->copied_seq = skb->h.th->seq;
#ifdef OLDWAY
if (skb->h.th->doff == 5) {
newsk->mtu = dev->mtu - HEADER_SIZE;
} else {
ptr =(unsigned char *)(skb->h.th + 1);
if (ptr[0] != 2 || ptr[1] != 4) {
newsk->mtu = dev->mtu - HEADER_SIZE;
} else {
newsk->mtu = min(ptr[2] * 256 + ptr[3] - HEADER_SIZE,
dev->mtu - HEADER_SIZE);
}
}
#else
/* Grab the ttl and tos values and use them */
newsk->ip_ttl=sk->ip_ttl;
newsk->ip_tos=skb->ip_hdr->tos;
tcp_options(newsk,skb->h.th);
#endif
buff = (struct sk_buff *) newsk->prot->wmalloc(newsk, MAX_SYN_SIZE, 1, GFP_ATOMIC);
if (buff == NULL) {
sk->err = -ENOMEM;
......@@ -1835,7 +1846,7 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
/* Put in the IP header and routing stuff. */
tmp = sk->prot->build_header(buff, newsk->saddr, newsk->daddr, &dev,
IPPROTO_TCP, NULL, MAX_SYN_SIZE);
IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
/* Something went wrong. */
if (tmp < 0) {
......@@ -1912,25 +1923,13 @@ tcp_close(struct sock *sk, int timeout)
sk->keepopen = 1;
sk->shutdown = SHUTDOWN_MASK;
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead)
sk->state_change(sk);
/* We need to flush the recv. buffs. */
if (skb_peek(&sk->rqueue) != NULL)
{
struct sk_buff *skb;
#ifdef OLD
struct sk_buff *skb2;
skb = skb_peek(&sk->rqueue);
do {
skb2 =(struct sk_buff *)skb->next;
/* if there is some real unread data, send a reset. */
if (skb->len > 0 &&
after(skb->h.th->seq + skb->len + 1, sk->copied_seq))
need_reset = 1;
kfree_skb(skb, FREE_WRITE);
skb = skb2;
} while(skb != sk->rqueue);
#else
if(sk->debug)
printk("Clean rcv queue\n");
while((skb=skb_dequeue(&sk->rqueue))!=NULL)
......@@ -1941,7 +1940,6 @@ tcp_close(struct sock *sk, int timeout)
}
if(sk->debug)
printk("Cleaned.\n");
#endif
}
sk->rqueue = NULL;
......@@ -1999,7 +1997,7 @@ tcp_close(struct sock *sk, int timeout)
/* Put in the IP header and routing stuff. */
tmp = prot->build_header(buff,sk->saddr, sk->daddr, &dev,
IPPROTO_TCP, sk->opt,
sizeof(struct tcphdr));
sizeof(struct tcphdr),sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
kfree_skb(buff,FREE_WRITE);
DPRINTF((DBG_TCP, "Unable to build header for fin.\n"));
......@@ -2088,7 +2086,7 @@ tcp_write_xmit(struct sock *sk)
if (before(skb->h.seq, sk->rcv_ack_seq +1)) {
sk->retransmits = 0;
kfree_skb(skb, FREE_WRITE);
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->write_space(sk);
} else {
sk->prot->queue_xmit(sk, skb->dev, skb, skb->free);
}
......@@ -2185,30 +2183,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
if (sk->packets_out > 0) sk->packets_out--;
/* We may need to remove this from the dev send list. */
if (skb->next != NULL) {
#ifdef OLD_WAY
int i;
if (skb->next != skb) {
skb->next->prev = skb->prev;
skb->prev->next = skb->next;
}
for(i = 0; i < DEV_NUMBUFFS; i++) {
if (skb->dev->buffs[i] == skb) {
if (skb->next == skb)
skb->dev->buffs[i] = NULL;
else
skb->dev->buffs[i] = skb->next;
break;
}
}
if (arp_q == skb) {
if (skb->next == skb) arp_q = NULL;
else arp_q = skb->next;
}
#else
skb_unlink(skb);
#endif
}
/* Now add it to the write_queue. */
skb->magic = TCP_WRITE_QUEUE_MAGIC;
......@@ -2272,7 +2247,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
sk->send_head, sk->send_head->h.seq, ack));
/* Wake up the process, it can probably write more. */
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->write_space(sk);
oskb = sk->send_head;
......@@ -2310,7 +2285,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
sti();
oskb->magic = 0;
kfree_skb(oskb, FREE_WRITE); /* write. */
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->write_space(sk);
} else {
break;
}
......@@ -2330,7 +2305,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
if (sk->send_head == NULL && sk->ack_backlog == 0 &&
sk->state != TCP_TIME_WAIT && !sk->keepopen) {
DPRINTF((DBG_TCP, "Nothing to do, going to sleep.\n"));
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->write_space(sk);
if (sk->keepopen)
reset_timer(sk, TIME_KEEPOPEN, TCP_TIMEOUT_LEN);
......@@ -2355,7 +2330,8 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
/* See if we are done. */
if (sk->state == TCP_TIME_WAIT) {
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead)
sk->state_change(sk);
if (sk->rcv_ack_seq == sk->send_seq && sk->acked_seq == sk->fin_seq) {
flag |= 1;
sk->state = TCP_CLOSE;
......@@ -2364,7 +2340,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
}
if (sk->state == TCP_LAST_ACK || sk->state == TCP_FIN_WAIT2) {
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->state_change(sk);
if (sk->rcv_ack_seq == sk->send_seq) {
flag |= 1;
if (sk->acked_seq != sk->fin_seq) {
......@@ -2421,13 +2397,13 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
if (sk->shutdown & RCV_SHUTDOWN) {
sk->acked_seq = th->seq + skb->len + th->syn + th->fin;
tcp_reset(sk->saddr, sk->daddr, skb->h.th,
sk->prot, NULL, skb->dev);
sk->prot, NULL, skb->dev, sk->ip_tos, sk->ip_ttl);
sk->state = TCP_CLOSE;
sk->err = EPIPE;
sk->shutdown = SHUTDOWN_MASK;
DPRINTF((DBG_TCP, "tcp_data: closing socket - %X\n", sk));
kfree_skb(skb, FREE_READ);
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->state_change(sk);
return(0);
}
......@@ -2523,7 +2499,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
/* When we ack the fin, we turn on the RCV_SHUTDOWN flag. */
if (skb->h.th->fin) {
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->state_change(sk);
sk->shutdown |= RCV_SHUTDOWN;
}
......@@ -2541,7 +2517,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
*/
if (skb2->h.th->fin) {
sk->shutdown |= RCV_SHUTDOWN;
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->state_change(sk);
}
/* Force an immediate ack. */
......@@ -2614,7 +2590,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
if (!sk->dead) {
if(sk->debug)
printk("Data wakeup.\n");
wake_up(sk->sleep);
sk->data_ready(sk,0);
} else {
DPRINTF((DBG_TCP, "data received on dead socket.\n"));
}
......@@ -2626,7 +2602,7 @@ tcp_data(struct sk_buff *skb, struct sock *sk,
/* tcp_send_ack(sk->send_seq, sk->acked_seq, sk, th, saddr); */
sk->shutdown = SHUTDOWN_MASK;
sk->state = TCP_LAST_ACK;
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead) sk->state_change(sk);
}
return(0);
......@@ -2639,7 +2615,8 @@ tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long saddr)
extern int kill_pg(int pg, int sig, int priv);
extern int kill_proc(int pid, int sig, int priv);
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead)
sk->data_ready(sk,0);
if (sk->urginline) {
th->urg = 0;
......@@ -2662,7 +2639,7 @@ tcp_urg(struct sock *sk, struct tcphdr *th, unsigned long saddr)
}
/* This deals with incoming fins. */
/* This deals with incoming fins. 'Linus at 9 O'clock' 8-) */
static int
tcp_fin(struct sock *sk, struct tcphdr *th,
unsigned long saddr, struct device *dev)
......@@ -2671,7 +2648,7 @@ tcp_fin(struct sock *sk, struct tcphdr *th,
sk, th, saddr, dev));
if (!sk->dead) {
wake_up(sk->sleep);
sk->state_change(sk);
}
switch(sk->state) {
......@@ -2813,7 +2790,7 @@ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
/* Put in the IP header and routing stuff. */
/* We need to build the routing stuff fromt the things saved in skb. */
tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
IPPROTO_TCP, NULL, MAX_SYN_SIZE);
IPPROTO_TCP, NULL, MAX_SYN_SIZE,sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
release_sock(sk);
......@@ -2862,7 +2839,7 @@ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
/* This functions checks to see if the tcp header is actually acceptible. */
static int
tcp_sequence(struct sock *sk, struct tcphdr *th, short len,
struct options *opt, unsigned long saddr)
struct options *opt, unsigned long saddr, struct device *dev)
{
/*
* This isn't quite right. sk->acked_seq could be more recent
......@@ -2882,6 +2859,19 @@ tcp_sequence(struct sock *sk, struct tcphdr *th, short len,
}
DPRINTF((DBG_TCP, "tcp_sequence: rejecting packet.\n"));
/*
* Send a reset if we get something not ours and we are
* unsynchronized. Note: We don't do anything to our end. We
* are just killing the bogus remote connection then we will
* connect again and it will work (with luck).
*/
if(sk->state==TCP_SYN_SENT||sk->state==TCP_SYN_RECV)
{
tcp_reset(sk->saddr,sk->daddr,th,sk->prot,NULL,dev, sk->ip_tos,sk->ip_ttl);
return(1);
}
/*
* If it's too far ahead, send an ack to let the
* other end know what we expect.
......@@ -2969,7 +2959,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
{
th->seq = ntohl(th->seq);
/* So reset is always called with th->seq in host order */
tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev);
tcp_reset(daddr, saddr, th, &tcp_prot, opt,dev,skb->ip_hdr->tos,255);
}
skb->sk = NULL;
kfree_skb(skb, FREE_READ);
......@@ -3042,7 +3032,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK;
if (!sk->dead) {
wake_up(sk->sleep);
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
......@@ -3054,7 +3044,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: bad checksum\n");
case TCP_FIN_WAIT1:
case TCP_FIN_WAIT2:
case TCP_TIME_WAIT:
if (!tcp_sequence(sk, th, len, opt, saddr)) {
if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {
if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
if(!th->rst)
tcp_send_ack(sk->send_seq, sk->acked_seq,
......@@ -3077,33 +3067,32 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
* A reset with a fin just means that
* the data was not all read.
*/
/* The comment above appears completely bogus --clh */
/* if (!th->fin) { */
sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK;
if (!sk->dead) {
wake_up(sk->sleep);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
/* } */
sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK;
if (!sk->dead) {
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (
#if 0
if (opt && (opt->security != 0 ||
opt->compartment != 0 || th->syn)) {
if ((opt && (opt->security != 0 ||
opt->compartment != 0)) ||
#endif
th->syn) {
sk->err = ECONNRESET;
sk->state = TCP_CLOSE;
sk->shutdown = SHUTDOWN_MASK;
tcp_reset(daddr, saddr, th, sk->prot, opt,dev);
tcp_reset(daddr, saddr, th, sk->prot, opt,dev, sk->ip_tos,sk->ip_ttl);
if (!sk->dead) {
wake_up(sk->sleep);
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
#endif
if (th->ack) {
if (!tcp_ack(sk, th, saddr, len)) {
kfree_skb(skb, FREE_READ);
......@@ -3119,13 +3108,14 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
}
}
if (th->fin && tcp_fin(sk, th, saddr, dev)) {
if (tcp_data(skb, sk, saddr, len)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
}
if (tcp_data(skb, sk, saddr, len)) {
/* Moved: you must do data then fin bit */
if (th->fin && tcp_fin(sk, th, saddr, dev)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
......@@ -3145,7 +3135,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
if (!th->rst) {
if (!th->ack)
th->ack_seq = 0;
tcp_reset(daddr, saddr, th, sk->prot, opt,dev);
tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
......@@ -3158,7 +3148,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
return(0);
}
if (th->ack) {
tcp_reset(daddr, saddr, th, sk->prot, opt,dev);
tcp_reset(daddr, saddr, th, sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
......@@ -3189,7 +3179,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
return(0);
default:
if (!tcp_sequence(sk, th, len, opt, saddr)) {
if (!tcp_sequence(sk, th, len, opt, saddr,dev)) {
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
......@@ -3202,7 +3192,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
sk->shutdown = SHUTDOWN_MASK;
sk->zapped = 1;
if (!sk->dead) {
wake_up(sk->sleep);
sk->state_change(sk);
}
kfree_skb(skb, FREE_READ);
release_sock(sk);
......@@ -3236,7 +3226,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
case TCP_SYN_SENT:
if (!tcp_ack(sk, th, saddr, len)) {
tcp_reset(daddr, saddr, th,
sk->prot, opt,dev);
sk->prot, opt,dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
......@@ -3261,7 +3251,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
case TCP_SYN_RECV:
if (!tcp_ack(sk, th, saddr, len)) {
tcp_reset(daddr, saddr, th,
sk->prot, opt, dev);
sk->prot, opt, dev,sk->ip_tos,sk->ip_ttl);
kfree_skb(skb, FREE_READ);
release_sock(sk);
return(0);
......@@ -3277,7 +3267,7 @@ if (inet_debug == DBG_SLIP) printk("\rtcp_rcv: not in seq\n");
sk->dummy_th.dest = th->source;
sk->copied_seq = sk->acked_seq-1;
if (!sk->dead) {
wake_up(sk->sleep);
sk->state_change(sk);
}
/*
......@@ -3354,7 +3344,7 @@ tcp_write_wakeup(struct sock *sk)
/* Put in the IP header and routing stuff. */
tmp = sk->prot->build_header(buff, sk->saddr, sk->daddr, &dev,
IPPROTO_TCP, sk->opt, MAX_ACK_SIZE);
IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);
if (tmp < 0) {
sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);
return;
......@@ -3389,6 +3379,72 @@ tcp_write_wakeup(struct sock *sk)
sk->prot->queue_xmit(sk, dev, buff, 1);
}
/*
* Socket option code for TCP.
*/
int tcp_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen)
{
int val,err;
if(level!=SOL_TCP)
return ip_setsockopt(sk,level,optname,optval,optlen);
if (optval == NULL)
return(-EINVAL);
err=verify_area(VERIFY_READ, optval, sizeof(int));
if(err)
return err;
val = get_fs_long((unsigned long *)optval);
switch(optname)
{
case TCP_MSS:
if(val<200||val>2048)
return -EINVAL;
sk->mss=val;
return 0;
case TCP_NODELAY:
/* Ready for Johannes delayed ACK code */
return 0;
default:
return(-ENOPROTOOPT);
}
}
int tcp_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen)
{
int val,err;
if(level!=SOL_TCP)
return ip_getsockopt(sk,level,optname,optval,optlen);
switch(optname)
{
case TCP_MSS:
val=sk->mss;
break;
case TCP_NODELAY:
val=1; /* Until Johannes stuff is in */
break;
default:
return(-ENOPROTOOPT);
}
err=verify_area(VERIFY_WRITE, optlen, sizeof(int));
if(err)
return err;
put_fs_long(sizeof(int),(unsigned long *) optlen);
err=verify_area(VERIFY_WRITE, optval, sizeof(int));
if(err)
return err;
put_fs_long(val,(unsigned long *)optval);
return(0);
}
struct proto tcp_prot = {
sock_wmalloc,
......@@ -3414,6 +3470,8 @@ struct proto tcp_prot = {
tcp_ioctl,
NULL,
tcp_shutdown,
tcp_setsockopt,
tcp_getsockopt,
128,
0,
{NULL,},
......
......@@ -141,7 +141,7 @@ net_timer (unsigned long data)
sk->state = TCP_CLOSE;
delete_timer (sk);
/* Kill the ARP entry in case the hardware has changed. */
arp_destroy (sk->daddr);
arp_destroy_maybe (sk->daddr);
if (!sk->dead)
wake_up (sk->sleep);
sk->shutdown = SHUTDOWN_MASK;
......@@ -167,7 +167,7 @@ net_timer (unsigned long data)
if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
DPRINTF ((DBG_TMR, "timer.c TIME_WRITE time-out 1\n"));
arp_destroy (sk->daddr);
arp_destroy_maybe (sk->daddr);
ip_route_check (sk->daddr);
}
if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
......@@ -198,14 +198,14 @@ net_timer (unsigned long data)
if ((sk->state == TCP_ESTABLISHED && sk->retransmits && !(sk->retransmits & 7))
|| (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR1)) {
DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 1\n"));
arp_destroy (sk->daddr);
arp_destroy_maybe (sk->daddr);
ip_route_check (sk->daddr);
release_sock (sk);
break;
}
if (sk->state != TCP_ESTABLISHED && sk->retransmits > TCP_RETR2) {
DPRINTF ((DBG_TMR, "timer.c TIME_KEEPOPEN time-out 2\n"));
arp_destroy (sk->daddr);
arp_destroy_maybe (sk->daddr);
sk->err = ETIMEDOUT;
if (sk->state == TCP_FIN_WAIT1 || sk->state == TCP_FIN_WAIT2) {
sk->state = TCP_TIME_WAIT;
......
......@@ -30,10 +30,11 @@
* bug no longer crashes it.
* Fred Van Kempen : Net2e support for sk->broadcast.
* Alan Cox : Uses skb_free_datagram
* Alan Cox : Added get/set sockopt support.
* Alan Cox : Broadcasting without option set returns EACCES.
* Alan Cox : No wakeup calls. Instead we now use the callbacks.
* Alan Cox : Use ip_tos and ip_ttl
*
* To Do:
* Verify all the error codes from UDP operations match the
* BSD behaviour, since thats effectively the formal spec.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
......@@ -114,7 +115,7 @@ sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th
if (err < 0) /* As per the calling spec */
{
sk->err = -err;
wake_up(sk->sleep); /* User process wakes to see error */
sk->error_report(sk); /* User process wakes to see error */
return;
}
......@@ -130,7 +131,7 @@ sport=%d,dport=%d", err, header, daddr, saddr, protocol, (int)th->source,(int)th
if (icmp_err_convert[err & 0xff].fatal && sk->state == TCP_ESTABLISHED) {
sk->err=ECONNREFUSED;
}
wake_up(sk->sleep);
sk->error_report(sk);
}
......@@ -249,7 +250,7 @@ udp_send(struct sock *sk, struct sockaddr_in *sin,
DPRINTF((DBG_UDP, "UDP: >> IP_Header: %X -> %X dev=%X prot=%X len=%d\n",
saddr, sin->sin_addr.s_addr, dev, IPPROTO_UDP, skb->mem_len));
tmp = sk->prot->build_header(skb, saddr, sin->sin_addr.s_addr,
&dev, IPPROTO_UDP, sk->opt, skb->mem_len);
&dev, IPPROTO_UDP, sk->opt, skb->mem_len,sk->ip_tos,sk->ip_ttl);
skb->sk=sk; /* So memory is freed correctly */
if (tmp < 0 ) {
......@@ -335,7 +336,7 @@ udp_sendto(struct sock *sk, unsigned char *from, int len, int noblock,
}
if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
return -ENETUNREACH; /* Must turn broadcast on first */
return -EACCES; /* Must turn broadcast on first */
sk->inuse = 1;
/* Send the packet. */
......@@ -522,7 +523,7 @@ udp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
return(-EAFNOSUPPORT);
if(!sk->broadcast && chk_addr(sin.sin_addr.s_addr)==IS_BROADCAST)
return -ENETUNREACH; /* Must turn broadcast on first */
return -EACCES; /* Must turn broadcast on first */
sk->daddr = sin.sin_addr.s_addr;
sk->dummy_th.dest = sin.sin_port;
......@@ -604,8 +605,9 @@ udp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
skb->len = len - sizeof(*uh);
if (!sk->dead) wake_up(sk->sleep);
if (!sk->dead)
sk->data_ready(sk,skb->len);
release_sock(sk);
return(0);
}
......@@ -635,6 +637,8 @@ struct proto udp_prot = {
udp_ioctl,
NULL,
NULL,
ip_setsockopt,
ip_getsockopt,
128,
0,
{NULL,},
......
......@@ -12,11 +12,12 @@
*
* Fixes:
* Alan Cox : Verify Area
* NET2E Team : Page fault locks
*
* BUGS
* Page faults on read while another process reads could lose data.
* Page faults on write happen to interleave data (probably not allowed)
* with any other simultaneous writers on the socket but dont cause harm.
* To Do:
*
* Change to the NET2E3 code for Unix domain sockets in general. The
* read/write logic is much better and cleaner.
*
*
* This program is free software; you can redistribute it and/or
......@@ -141,6 +142,29 @@ sockaddr_un_printk(struct sockaddr_un *sockun, int sockaddr_len)
}
/* Support routines doing anti page fault locking
* FvK & Matt Dillon (borrowed From NET2E3)
*/
/*
* Locking for unix-domain sockets. We don't use the socket structure's
* wait queue because it is allowed to 'go away' outside of our control,
* whereas unix_proto_data structures stick around.
*/
void unix_lock(struct unix_proto_data *upd)
{
while (upd->lock_flag)
sleep_on(&upd->wait);
upd->lock_flag = 1;
}
void unix_unlock(struct unix_proto_data *upd)
{
upd->lock_flag = 0;
wake_up(&upd->wait);
}
/* don't have to do anything. */
static int
unix_proto_listen(struct socket *sock, int backlog)
......@@ -598,6 +622,8 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
* Copy from the read buffer into the user's buffer,
* watching for wraparound. Then we wake up the writer.
*/
unix_lock(upd);
do {
int part, cando;
......@@ -612,7 +638,10 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
dprintf(1, "UNIX: read: avail=%d, todo=%d, cando=%d\n",
avail, todo, cando);
if((er=verify_area(VERIFY_WRITE,ubuf,cando))<0)
{
unix_unlock(upd);
return er;
}
memcpy_tofs(ubuf, upd->buf + upd->bp_tail, cando);
upd->bp_tail =(upd->bp_tail + cando) &(BUF_SIZE-1);
ubuf += cando;
......@@ -620,6 +649,7 @@ unix_proto_read(struct socket *sock, char *ubuf, int size, int nonblock)
if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait);
avail = UN_BUF_AVAIL(upd);
} while(todo && avail);
unix_unlock(upd);
return(size - todo);
}
......@@ -666,6 +696,9 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
* Copy from the user's buffer to the write buffer,
* watching for wraparound. Then we wake up the reader.
*/
unix_lock(pupd);
do {
int part, cando;
......@@ -681,6 +714,7 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
*/
if (sock->state == SS_DISCONNECTING) {
send_sig(SIGPIPE, current, 1);
unix_unlock(pupd);
return(-EPIPE);
}
if ((cando = todo) > space) cando = space;
......@@ -689,7 +723,10 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
space, todo, cando);
er=verify_area(VERIFY_READ, ubuf, cando);
if(er)
{
unix_unlock(pupd);
return er;
}
memcpy_fromfs(pupd->buf + pupd->bp_head, ubuf, cando);
pupd->bp_head =(pupd->bp_head + cando) &(BUF_SIZE-1);
ubuf += cando;
......@@ -697,6 +734,7 @@ unix_proto_write(struct socket *sock, char *ubuf, int size, int nonblock)
if (sock->state == SS_CONNECTED) wake_up(sock->conn->wait);
space = UN_BUF_SPACE(pupd);
} while(todo && space);
unix_unlock(pupd);
return(size - todo);
}
......@@ -842,6 +880,8 @@ unix_ioctl(struct inode *inode, struct file *file,
}
static struct file_operations unix_fops = {
NULL, /* LSEEK */
NULL, /* READ */
......
......@@ -35,6 +35,8 @@ struct unix_proto_data {
int bp_head, bp_tail;
struct inode *inode;
struct unix_proto_data *peerupd;
struct wait_queue *wait; /* Lock across page faults (FvK) */
int lock_flag;
};
extern struct unix_proto_data unix_datas[NSOCKETS];
......
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