Subversion Repositories shark

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
961 pj 1
/*
2
 * Project: S.Ha.R.K.
3
 *
4
 * Coordinators:
5
 *   Giorgio Buttazzo    <giorgio@sssup.it>
6
 *   Paolo Gai           <pj@gandalf.sssup.it>
7
 *
8
 * Authors     :
9
 *   Paolo Gai           <pj@gandalf.sssup.it>
10
 *   Massimiliano Giorgi <massy@gandalf.sssup.it>
11
 *   Luca Abeni          <luca@gandalf.sssup.it>
12
 *   (see the web pages for full authors list)
13
 *
14
 * ReTiS Lab (Scuola Superiore S.Anna - Pisa - Italy)
15
 *
16
 * http://www.sssup.it
17
 * http://retis.sssup.it
18
 * http://shark.sssup.it
19
 */
20
 
21
/**
22
 ------------
23
 CVS :        $Id: hartport.c,v 1.1 2005-02-25 10:53:41 pj Exp $
24
 
25
 File:        $File$
26
 Revision:    $Revision: 1.1 $
27
 Last update: $Date: 2005-02-25 10:53:41 $
28
 ------------
29
 
30
 This file contains the Hartik 3.3.1 Port functions
31
 
32
 Author:      Giuseppe Lipari
33
 Date:        2/7/96
34
 
35
 File:  Port.C (renamed to hartport.c)
36
 Revision:    1.4
37
 
38
**/
39
 
40
/*
41
 * Copyright (C) 2000 Paolo Gai
42
 *
43
 * This program is free software; you can redistribute it and/or modify
44
 * it under the terms of the GNU General Public License as published by
45
 * the Free Software Foundation; either version 2 of the License, or
46
 * (at your option) any later version.
47
 *
48
 * This program is distributed in the hope that it will be useful,
49
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
50
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
51
 * GNU General Public License for more details.
52
 *
53
 * You should have received a copy of the GNU General Public License
54
 * along with this program; if not, write to the Free Software
55
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
56
 *
57
 */
58
 
59
 
60
#include <hartport/hartport/hartport.h>
61
#include <kernel/config.h>
62
#include <ll/ll.h>
63
#include <ll/string.h>
64
#include <ll/stdio.h>
65
#include <kernel/model.h>
66
#include <kernel/const.h>
67
#include <sys/types.h>
68
#include <kernel/descr.h>
69
#include <errno.h>
70
#include <kernel/var.h>
71
#include <kernel/func.h>
72
 
73
 
74
static sem_t hash_mutex;
75
 
76
#define __PORT_DBG__
77
 
78
struct hash_port {
79
        char    name[MAX_PORT_NAME];
80
        PID     port_index;
81
        BYTE    blocked;
82
        sem_t   sem;
83
        BYTE    valid;
84
        int     port_int;
85
};
86
 
87
struct port_ker {
88
        BYTE    valid;
89
        int     next;
90
        WORD    dim_block;
91
        BYTE    *mem_block;
92
        BYTE    *head;
93
        BYTE    *tail;
94
        BYTE    type;
95
        sem_t   mutex;
96
        sem_t   pieno;
97
        sem_t   vuoto;
98
};
99
 
100
struct port_com {
101
        BYTE    valid;
102
        int     next;
103
        PID     proc_id;
104
        BYTE    access;
105
        int     port_index;
106
        WORD    dim_mes;
107
        int     h_index;
108
};
109
 
110
struct hash_port        htable[MAX_HASH_ENTRY];
111
struct port_ker         port_des[MAX_PORT];
112
struct port_com         port_int[MAX_PORT_INT];
113
int     freeportdes;
114
int     freeportint;
115
 
116
static int port_installed = 0;
117
 
118
 
119
/*----------------------------------------------------------------------*/
120
/* port_init() : inizializza le strutture delle porte; da chiamare      */
121
/* dentro __hartik_register_levels__().                                 */
122
/*----------------------------------------------------------------------*/
123
void HARTPORT_init(void)
124
{
125
    int i;
126
 
127
    if (port_installed)
128
      return;
129
    port_installed = 1;
130
 
131
    /* Init hash table                  */
132
    for (i = 0; i < MAX_HASH_ENTRY; i++) {
133
        htable[i].valid = FALSE;
134
        htable[i].port_int = NIL;
135
        htable[i].blocked = 0;
136
    }
137
 
138
    /* mutex sem on the hash table              */
139
    sem_init(&hash_mutex, 0, 1);
140
 
141
    /* init the port descriptor table   */
142
    for (i = 0; i < MAX_PORT - 1; i++) {
143
        port_des[i].next = i+1;
144
        port_des[i].valid = FALSE;
145
    }
146
    port_des[MAX_PORT - 1].next = NIL;
147
    port_des[MAX_PORT - 1].valid = FALSE;
148
    freeportdes = 0;
149
 
150
    /* Init the port interface table    */
151
    for (i = 0; i < MAX_PORT_INT - 1; i++) {
152
        port_int[i].next = i+1;
153
        port_int[i].valid = FALSE;
154
    }
155
    port_int[MAX_PORT_INT - 1].next = NIL;
156
    port_int[MAX_PORT_INT - 1].valid = FALSE;
157
//    for (i = PORT_NO_MORE_DESCR; i <= PORT_UNVALID_DESCR; i++)
158
//        exc_set(i,port_exception);
159
    freeportint = 0;
160
}
161
 
162
 
163
/*----------------------------------------------------------------------*/
164
/* hash_fun() : address hash table                                      */
165
/*----------------------------------------------------------------------*/
166
static int hash_fun(char *name)
167
{
168
    return (*name % MAX_HASH_ENTRY);
169
}
170
 
171
 
172
/*----------------------------------------------------------------------*/
173
/* getfreedes : restituisce l'indice di un descrittore di porta libero  */
174
/*----------------------------------------------------------------------*/
175
static int getfreedes(void)
176
{
177
    int p;
178
    SYS_FLAGS f;
179
 
180
    f = kern_fsave();
181
    if (freeportdes == NIL) {
182
        errno = EPORT_NO_MORE_DESCR;
183
        kern_frestore(f);
184
        return -1;
185
    }
186
    p = freeportdes;
187
    freeportdes = port_des[p].next;
188
    kern_frestore(f);
189
    return(p);
190
}        
191
 
192
/*----------------------------------------------------------------------*/
193
/* ungetdes() : mette il descrittore tra quelli disponibile             */
194
/*----------------------------------------------------------------------*/
195
static void ungetdes(int pd)
196
{
197
    SYS_FLAGS f;
198
 
199
    f = kern_fsave();
200
    port_des[pd].next = freeportdes;
201
    freeportdes = pd;
202
    kern_frestore(f);
203
}
204
 
205
/*----------------------------------------------------------------------*/
206
/* get freeint : restituisce una interfaccia di porta libera            */
207
/*----------------------------------------------------------------------*/
208
static int getfreeint(void)
209
{
210
    int p;
211
    SYS_FLAGS f;                                                                                                                      
212
    f = kern_fsave();
213
    if (freeportint == NIL) {
214
        errno = EPORT_NO_MORE_INTERF;
215
        kern_frestore(f);
216
        return -1;
217
    }
218
    p = freeportint;
219
    freeportint = port_int[p].next;
220
    kern_frestore(f);
221
    return(p);
222
}      
223
 
224
/*----------------------------------------------------------------------*/
225
/* ungetint : rende disponibile l'interfaccia di porta specificata      */
226
/*----------------------------------------------------------------------*/
227
static void ungetint(int pi)
228
{
229
    SYS_FLAGS f;
230
 
231
    f = kern_fsave();
232
    port_int[pi].next = freeportint;
233
    freeportint = pi;
234
    kern_frestore(f);
235
}
236
 
237
/*----------------------------------------------------------------------*/
238
/* port_create(): Apre la porta specificata dalla stringa, effettuando  */
239
/* automaticamente il collegamento                                      */
240
/* WARNING : La funzione e' bloccante per la mutua esclusione sulla     */
241
/* hash table                                                   */
242
/*----------------------------------------------------------------------*/
243
PORT port_create(char *name, int dim_mes, int num_mes, BYTE type, BYTE access)
244
{
245
    int i, pd, pi;
246
    WORD letti = 0;
247
    BYTE flag = FALSE;
248
    SYS_FLAGS f;
249
 
250
/*
251
   Prendo un descrittore di porta.
252
*/
253
    #ifdef __PORT_DBG__
254
        if ((type == MAILBOX) && (access == WRITE)) {
255
           errno = EPORT_INCOMPAT_MESSAGE;
256
           return -1;
257
        }
258
        if ((type == STICK  ) && (access == READ )) {
259
           errno = EPORT_INCOMPAT_MESSAGE;
260
           return -1;
261
        }
262
    #endif
263
    pd = getfreedes();
264
    if (pd == -1) return -1;
265
/*
266
   Devo andare in mutua esclusione sulla tabella di hash ! Poiche' questa
267
   viene acceduta dalle openport e dalle connect sono sicuro che una
268
   connect non puo' interrompere una openport.
269
*/
270
    sem_wait(&hash_mutex);
271
/*
272
   Scorro la tabella di hash fino a trovare un'entrata libera, oppure
273
   una entrata occupata in precedenza da una connect che ci si e' bloccata
274
   sopra (blocked > 0). Se ne trovo una gia' aperta da un'altra openport
275
   esco con errore. Lo stesso se sono state occupate tutte le entrate della
276
   tabella (tramite la var. letti);
277
*/
278
    i = hash_fun(name);
279
    while (!flag) {
280
        if (htable[i].valid == FALSE) flag = TRUE;
281
        else {
282
            if (strcmp(htable[i].name,name) == 0) {
283
                if (htable[i].blocked == 0) {
284
                    errno = EPORT_ALREADY_OPEN;
285
                    sem_post(&hash_mutex);
286
                    return -1;
287
                }
288
                else flag = TRUE;
289
            }
290
            else {
291
                i = (i+1) % MAX_HASH_ENTRY;
292
                letti++;
293
            }
294
        }
295
        if (letti > MAX_HASH_ENTRY-1) {
296
            errno = EPORT_NO_MORE_HASHENTRY;
297
            sem_post(&hash_mutex);
298
            return -1;
299
        }
300
 
301
    }
302
    htable[i].valid = TRUE;
303
    strcpy(htable[i].name, name);
304
 
305
    htable[i].port_index = pd;
306
/*
307
   A questo punto inizializzo tutta la struttura del descrittore di porta
308
*/
309
    if (type == STICK)  port_des[pd].dim_block = dim_mes;
310
    else port_des[pd].dim_block = dim_mes * num_mes;
311
 
312
    f = kern_fsave();
313
    port_des[pd].mem_block = kern_alloc(port_des[pd].dim_block);
314
    kern_frestore(f);
315
    if (port_des[pd].mem_block == NULL) {
316
        errno = EPORT_2_CONNECT;
317
        sem_post(&hash_mutex);
318
        return -1;
319
    }
320
 
321
    port_des[pd].head = port_des[pd].tail = port_des[pd].mem_block;
322
 
323
    sem_init(&port_des[pd].mutex,0,1);
324
    sem_init(&port_des[pd].pieno,0,port_des[pd].dim_block);
325
    sem_init(&port_des[pd].vuoto,0,0);
326
    port_des[pd].type = type;
327
/*
328
   Prendo e inizializzo la struttura dell'interfaccia di porta verso il
329
   processo (e' la varibile pi quella che restituisco)
330
*/
331
    pi = getfreeint();
332
    if (pi == -1) {
333
        sem_post(&hash_mutex);
334
        return -1;
335
    }
336
 
337
/*  port_int[pi].proc_id = exec_shadow; */
338
    port_int[pi].access = access;
339
    port_int[pi].port_index = pd;
340
    port_int[pi].dim_mes = dim_mes;
341
    port_int[pi].next = NIL;
342
    port_int[pi].h_index = i;
343
    port_des[pd].valid = TRUE;
344
    port_int[pi].valid = TRUE;
345
/*
346
   Sblocco eventuali processi che avevano fatto la connect nella coda
347
   semaforica che sta nell'entrata relativa della hash table !
348
*/
349
    if (htable[i].blocked > 0) {
350
        sem_xpost(&htable[i].sem, htable[i].blocked);
351
        htable[i].blocked = 0;
352
        sem_destroy(&htable[i].sem);
353
    }
354
/*
355
   Infine libero la mutua esclusione.
356
*/
357
    sem_post(&hash_mutex);
358
    return (pi);
359
}
360
 
361
/*----------------------------------------------------------------------*/
362
/* port_connect(): collega la porta specificata dalla stringa.          */
363
/* WARNING : La funzione e' bloccante per la mutua esclusione sulle     */
364
/* strutture delle porte                                                */
365
/*----------------------------------------------------------------------*/
366
PORT port_connect(char *name, int dim_mes, BYTE type, BYTE access)
367
{
368
    int i, pi, pd, pn,letti = 0;
369
    BYTE flag = FALSE, create = FALSE;
370
 
371
    #ifdef __PORT_DBG__
372
        if ((type == MAILBOX) && (access == READ )) {
373
            errno = EPORT_INCOMPAT_MESSAGE;
374
            return -1;
375
        }
376
        if ((type == STICK  ) && (access == WRITE)) {
377
           errno = EPORT_INCOMPAT_MESSAGE;
378
           return -1;
379
        }
380
    #endif
381
/*
382
   Per prima cosa mi prendo una interfaccia di porta e la riempio
383
   parzialmente.       
384
*/
385
    pi = getfreeint();
386
    if (pi == -1) return -1;
387
/*  port_int[pi].proc_id = exec_shadow; */
388
    port_int[pi].access = access;
389
    port_int[pi].dim_mes = dim_mes;
390
    port_int[pi].next = NIL;
391
/*
392
   Mutua esclusione sulla tabella di hash
393
*/
394
    sem_wait(&hash_mutex);
395
/*
396
   Cerco il descrittore appropriato : Se la porta e' gia' stata aperta
397
   allora esco dal ciclo con flag = TRUE, create = FALSE, l'indice i che
398
   punta all'entrata della tabella di hash, e con htable[i].blocked = 0;
399
   In tutti gli altri casi significa che la porta non e' stata aperta e
400
   quindi devo bloccarmi.
401
*/
402
    i = hash_fun(name);
403
    while (!flag) {
404
        /* Devo crearla */
405
        if (htable[i].valid == FALSE) {
406
            flag = TRUE;
407
            create = TRUE;
408
        }
409
        /* l'ho trovata (ma non so ancora se e' stata aperta) */
410
        else if (strcmp(htable[i].name, name) == 0) flag = TRUE;
411
        /* scandisco l'entrata successiva */
412
        else {
413
            i = (i+1) % MAX_HASH_ENTRY;
414
            letti ++;
415
        }
416
        #ifdef __PORT_DBG__
417
            /* se ho scorso tutto l'array senza trovare nulla */
418
            if (letti > MAX_HASH_ENTRY) {
419
                errno = EPORT_NO_MORE_HASHENTRY;
420
                sem_post(&hash_mutex);
421
                return -1;
422
            }
423
        #endif
424
    }
425
 
426
/*
427
   Se devo aspettare che venga aperta (create = TRUE) allora mi blocco in
428
   attesa sul semaforo della htable. Per non avere troppi semafori inutili
429
   in giro, me lo creo sul momento.
430
*/
431
    if (create == TRUE) {
432
        htable[i].valid = TRUE;
433
        htable[i].blocked = 1;
434
        strcpy(htable[i].name, name);
435
        sem_init(&htable[i].sem, 0, 0);
436
        sem_post(&hash_mutex);
437
        sem_xwait(&htable[i].sem,1,BLOCK);
438
    }
439
/*
440
   Se invece si e' gia' bloccata un'altra connect sopra, mi blocco anch'io.
441
   in ogni caso devo liberare la mutua esclusione sulla hash dato che ho
442
   gia' fatto tutto quello che dovevo fare con la hash.
443
*/
444
    else {
445
        if (htable[i].blocked > 0) {
446
            htable[i].blocked++;
447
            sem_post(&hash_mutex);
448
            sem_xwait(&htable[i].sem,1,BLOCK);
449
        }
450
        else sem_post(&hash_mutex);
451
    }
452
/*
453
   Controlli di errore.
454
*/
455
    pd = htable[i].port_index;
456
    #ifdef __PORT_DBG__
457
        if (type != port_des[pd].type) {
458
            errno = EPORT_UNSUPPORTED_ACC;
459
            return -1;
460
        }
461
        if ((type == STICK) && (dim_mes != port_des[pd].dim_block)) {
462
            errno = EPORT_WRONG_OP;
463
            return -1;
464
        }
465
        if ((type != STICK) && (port_des[pd].dim_block % dim_mes) != 0) {
466
            errno = EPORT_WRONG_OP;
467
            return -1;
468
        }
469
    #endif
470
 
471
    sem_wait(&hash_mutex);
472
    pn = htable[i].port_int;
473
    if (pn != NIL) {
474
        #ifdef __PORT_DBG__
475
            if (type == STREAM) {
476
                errno = EPORT_WRONG_TYPE;
477
                sem_post(&hash_mutex);
478
                return -1;
479
            }
480
            if (dim_mes != port_int[pn].dim_mes) {
481
                errno = EPORT_WRONG_OP;
482
                sem_post(&hash_mutex);
483
                return -1;
484
            }
485
        #endif
486
        port_int[pi].next = htable[i].port_int;
487
        htable[i].port_int = pi;
488
    }
489
    else htable[i].port_int = pi;
490
    sem_post(&hash_mutex);
491
    port_int[pi].h_index = i;
492
    port_int[pi].port_index = pd;
493
    port_int[pi].valid = TRUE;
494
    return(pi);
495
}
496
 
497
/*----------------------------------------------------------------------*/
498
/* port_delete() : inversa di port_open, libera tutto                   */
499
/*----------------------------------------------------------------------*/
500
void port_delete(PORT pi)
501
{
502
    int i;
503
    struct port_ker *pd;
504
    struct port_com *pp;
505
    SYS_FLAGS f;
506
 
507
    pp = &port_int[pi];
508
    sem_wait(&hash_mutex);
509
    i = pp->h_index;
510
    pd = &port_des[htable[i].port_index];
511
    pd->valid = FALSE;
512
    sem_destroy(&pd->mutex);
513
    sem_destroy(&pd->pieno);
514
    sem_destroy(&pd->vuoto);
515
 
516
    f = kern_fsave();
517
    kern_free(pd->mem_block, pd->dim_block);
518
    kern_frestore(f);
519
 
520
    ungetdes(htable[i].port_index);
521
    pp->valid = FALSE;
522
    htable[i].port_int = pp->next;
523
    ungetint(pi);
524
    htable[i].valid = FALSE;
525
    sem_post(&hash_mutex);
526
}
527
 
528
/*----------------------------------------------------------------------*/
529
/* port_disconnect() : libera l'interfaccia di porta                    */
530
/*----------------------------------------------------------------------*/
531
void port_disconnect(PORT pi)
532
{
533
    sem_wait(&hash_mutex);
534
    if (htable[port_int[pi].h_index].valid == TRUE)
535
        htable[port_int[pi].h_index].port_int = port_int[pi].next;
536
    port_int[pi].valid = FALSE;
537
    ungetint(pi);
538
    sem_post(&hash_mutex);
539
}
540
 
541
/*----------------------------------------------------------------------*/
542
/* port_send() : Invia un messaggio alla porta                          */
543
/*----------------------------------------------------------------------*/
544
WORD port_send(PORT pi, void *msg, BYTE wait)
545
{
546
    struct port_ker *pd;
547
    struct port_com *pp;
548
 
549
    pp = &(port_int[pi]);
550
    pd = &(port_des[pp->port_index]);
551
 
552
    #ifdef __PORT_DBG__
553
        if (pp->access == READ) {
554
          errno = EPORT_WRONG_OP;
555
          return -1;
556
        }
557
        if (!pd->valid) {
558
          errno = EPORT_INVALID_DESCR;
559
          return -1;
560
        }
561
 
562
    #endif
563
 
564
    if (pd->type == STICK) sem_wait(&pd->mutex);
565
    else if (pd->type == STREAM) {
566
            if (sem_xwait(&pd->pieno,pp->dim_mes,wait)) return(FALSE);
567
    }
568
    else {
569
        if (sem_xwait(&pd->pieno, pp->dim_mes,wait)) return(FALSE);
570
        sem_wait(&pd->mutex);
571
    }
572
 
573
    memcpy(pd->head, msg, pp->dim_mes);
574
 
575
    pd->head += pp->dim_mes;
576
 
577
    if (pd->head >= (pd->mem_block + pd->dim_block))
578
        pd->head -= pd->dim_block;
579
 
580
    if (pd->type == STICK) sem_post(&pd->mutex);
581
    else if (pd->type == STREAM) sem_xpost(&pd->vuoto, pp->dim_mes);
582
    else {
583
        sem_xpost(&pd->vuoto, pp->dim_mes);
584
        sem_post(&pd->mutex);
585
    }
586
    return(TRUE);
587
}
588
 
589
/*----------------------------------------------------------------------*/
590
/* port_receive() : Riceve un messaggio dalla porta                     */
591
/*----------------------------------------------------------------------*/
592
WORD port_receive(PORT pi,void *msg,BYTE wait)
593
{
594
    struct port_ker *pd;
595
    struct port_com *pp;
596
 
597
    pp = &(port_int[pi]);
598
    pd = &(port_des[pp->port_index]);
599
 
600
    #ifdef __PORT_DBG__
601
        if (pp->access == WRITE) {
602
          errno = EPORT_WRONG_OP;
603
          return -1;
604
        }
605
        if (!pd->valid) {
606
          errno = EPORT_INVALID_DESCR;
607
          return -1;
608
        }
609
    #endif      
610
 
611
    if (pd->type == STICK) sem_wait(&pd->mutex);
612
    else if (pd->type == STREAM) {
613
         if (sem_xwait(&pd->vuoto,pp->dim_mes,wait)) return(FALSE);
614
    }
615
    else {
616
        if (sem_xwait(&pd->vuoto,pp->dim_mes,wait)) return(FALSE);
617
        sem_wait(&pd->mutex);
618
    }
619
 
620
    memcpy(msg, pd->tail, pp->dim_mes);
621
 
622
    pd->tail += pp->dim_mes;
623
    if (pd->tail >= (pd->mem_block + pd->dim_block))
624
        pd->tail -= pd->dim_block;
625
 
626
    if (pd->type == STICK) sem_post(&pd->mutex);
627
    else if (pd->type == STREAM) sem_xpost(&pd->pieno, pp->dim_mes);
628
    else {
629
        sem_xpost(&pd->pieno, pp->dim_mes);
630
        sem_post(&pd->mutex);
631
    }
632
    return(TRUE);
633
}
634
 
635
#ifdef __PORT_DBG__
636
 
637
void print_port(void)
638
{
639
    int i;
640
    struct port_ker *pd;
641
    struct port_com *pp;
642
 
643
/*
644
        kern_printf("Hash Table :\n");
645
        for (i=0; i<MAX_HASH_ENTRY; i++)
646
                kern_printf("%d\tvl: %d\tbl: %d\tpd: %d\t%s\n", i,
647
                        htable[i].valid, htable[i].blocked, htable[i].port_index,
648
                        htable[i].name);
649
*/
650
        kern_printf("Port des :\n");
651
        kern_printf("Free port des : %d\n", freeportdes);
652
        for (i=0; i<MAX_PORT_INT; i++)
653
            if (port_int[i].valid) {
654
                pp = &port_int[i];
655
                pd = &port_des[pp->port_index];
656
                kern_printf("%d %s  vt: %d      pn: %d\n",i,htable[pp->h_index].name,
657
                pd->vuoto,pd->pieno);
658
            }
659
/*
660
        kern_printf("Port int :\n");
661
        kern_printf("Free port int : %d\n", freeportint);
662
        for (i=0; i<MAX_PORT_INT; i++)
663
                kern_printf("%d  vl : %d  dm : %d  port_index : %d  proc_id : %d\n", i,
664
                        port_int[i].valid, port_int[i].dim_mes,
665
                        port_int[i].port_index, port_int[i].proc_id);
666
*/
667
}
668
 
669
void port_write(PORT p)
670
{
671
    struct port_ker *pd;
672
    struct port_com *pp;
673
    char msg[80];
674
 
675
    pp = &port_int[p];
676
    pd = &port_des[pp->port_index];
677
 
678
    kern_printf(msg,"%d  pd: %d   vt: %d      pn: %d ",p,pp->port_index,
679
        pd->vuoto,pd->pieno);
680
}
681
 
682
#endif