linux-bk/drivers/net/apne.c
<<
>>
Prefs
   1/*
   2 * Amiga Linux/68k 8390 based PCMCIA Ethernet Driver for the Amiga 1200
   3 *
   4 * (C) Copyright 1997 Alain Malek
   5 *                    (Alain.Malek@cryogen.com)
   6 *
   7 * ----------------------------------------------------------------------------
   8 *
   9 * This program is based on
  10 *
  11 * ne.c:       A general non-shared-memory NS8390 ethernet driver for linux
  12 *             Written 1992-94 by Donald Becker.
  13 *
  14 * 8390.c:     A general NS8390 ethernet driver core for linux.
  15 *             Written 1992-94 by Donald Becker.
  16 *
  17 * cnetdevice: A Sana-II ethernet driver for AmigaOS
  18 *             Written by Bruce Abbott (bhabbott@inhb.co.nz)
  19 *
  20 * ----------------------------------------------------------------------------
  21 *
  22 * This file is subject to the terms and conditions of the GNU General Public
  23 * License.  See the file COPYING in the main directory of the Linux
  24 * distribution for more details.
  25 *
  26 * ----------------------------------------------------------------------------
  27 *
  28 */
  29
  30
  31#include <linux/module.h>
  32#include <linux/kernel.h>
  33#include <linux/sched.h>
  34#include <linux/errno.h>
  35#include <linux/pci.h>
  36#include <linux/init.h>
  37#include <linux/delay.h>
  38#include <asm/system.h>
  39#include <asm/io.h>
  40
  41#include <linux/netdevice.h>
  42#include <linux/etherdevice.h>
  43
  44#include <asm/setup.h>
  45#include <asm/amigaints.h>
  46#include <asm/amigahw.h>
  47#include <asm/amigayle.h>
  48#include <asm/amipcmcia.h>
  49
  50#include "8390.h"
  51
  52/* ---- No user-serviceable parts below ---- */
  53
  54#define NE_BASE  (dev->base_addr)
  55#define NE_CMD                  0x00
  56#define NE_DATAPORT             0x10            /* NatSemi-defined port window offset. */
  57#define NE_RESET                0x1f            /* Issue a read to reset, a write to clear. */
  58#define NE_IO_EXTENT            0x20
  59
  60#define NE_EN0_ISR              0x07
  61#define NE_EN0_DCFG             0x0e
  62
  63#define NE_EN0_RSARLO           0x08
  64#define NE_EN0_RSARHI           0x09
  65#define NE_EN0_RCNTLO           0x0a
  66#define NE_EN0_RXCR             0x0c
  67#define NE_EN0_TXCR             0x0d
  68#define NE_EN0_RCNTHI           0x0b
  69#define NE_EN0_IMR              0x0f
  70
  71#define NE1SM_START_PG  0x20    /* First page of TX buffer */
  72#define NE1SM_STOP_PG   0x40    /* Last page +1 of RX ring */
  73#define NESM_START_PG   0x40    /* First page of TX buffer */
  74#define NESM_STOP_PG    0x80    /* Last page +1 of RX ring */
  75
  76
  77int apne_probe(struct net_device *dev);
  78static int apne_probe1(struct net_device *dev, int ioaddr);
  79
  80static int apne_open(struct net_device *dev);
  81static int apne_close(struct net_device *dev);
  82
  83static void apne_reset_8390(struct net_device *dev);
  84static void apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr,
  85                          int ring_page);
  86static void apne_block_input(struct net_device *dev, int count,
  87                                                                struct sk_buff *skb, int ring_offset);
  88static void apne_block_output(struct net_device *dev, const int count,
  89                                                        const unsigned char *buf, const int start_page);
  90static void apne_interrupt(int irq, void *dev_id, struct pt_regs *regs);
  91
  92static int init_pcmcia(void);
  93
  94/* IO base address used for nic */
  95
  96#define IOBASE 0x300
  97
  98/*
  99   use MANUAL_CONFIG and MANUAL_OFFSET for enabling IO by hand
 100   you can find the values to use by looking at the cnet.device
 101   config file example (the default values are for the CNET40BC card)
 102*/
 103
 104/*
 105#define MANUAL_CONFIG 0x20
 106#define MANUAL_OFFSET 0x3f8
 107
 108#define MANUAL_HWADDR0 0x00
 109#define MANUAL_HWADDR1 0x12
 110#define MANUAL_HWADDR2 0x34
 111#define MANUAL_HWADDR3 0x56
 112#define MANUAL_HWADDR4 0x78
 113#define MANUAL_HWADDR5 0x9a
 114*/
 115
 116#define WORDSWAP(a) ( (((a)>>8)&0xff) | ((a)<<8) )
 117
 118
 119static const char version[] =
 120    "apne.c:v1.1 7/10/98 Alain Malek (Alain.Malek@cryogen.ch)\n";
 121
 122static int apne_owned;  /* signal if card already owned */
 123
 124int __init apne_probe(struct net_device *dev)
 125{
 126#ifndef MANUAL_CONFIG
 127        char tuple[8];
 128#endif
 129
 130        if (apne_owned)
 131                return -ENODEV;
 132
 133        SET_MODULE_OWNER(dev);
 134
 135        if ( !(AMIGAHW_PRESENT(PCMCIA)) )
 136                return (-ENODEV);
 137                                
 138        printk("Looking for PCMCIA ethernet card : ");
 139                                        
 140        /* check if a card is inserted */
 141        if (!(PCMCIA_INSERTED)) {
 142                printk("NO PCMCIA card inserted\n");
 143                return (-ENODEV);
 144        }
 145                                                                                                
 146        /* disable pcmcia irq for readtuple */
 147        pcmcia_disable_irq();
 148
 149#ifndef MANUAL_CONFIG
 150        if ((pcmcia_copy_tuple(CISTPL_FUNCID, tuple, 8) < 3) ||
 151                (tuple[2] != CISTPL_FUNCID_NETWORK)) {
 152                printk("not an ethernet card\n");
 153                return (-ENODEV);
 154        }
 155#endif
 156
 157        printk("ethernet PCMCIA card inserted\n");
 158
 159        if (init_pcmcia())
 160                return apne_probe1(dev, IOBASE);
 161        else
 162                return (-ENODEV);
 163
 164}
 165
 166static int __init apne_probe1(struct net_device *dev, int ioaddr)
 167{
 168    int i;
 169    unsigned char SA_prom[32];
 170    int wordlength = 2;
 171    const char *name = NULL;
 172    int start_page, stop_page;
 173#ifndef MANUAL_HWADDR0
 174    int neX000, ctron;
 175#endif
 176    static unsigned version_printed;
 177 
 178    if (ei_debug  &&  version_printed++ == 0)
 179        printk(version);
 180
 181    printk("PCMCIA NE*000 ethercard probe");
 182
 183    /* Reset card. Who knows what dain-bramaged state it was left in. */
 184    {   unsigned long reset_start_time = jiffies;
 185
 186        outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
 187
 188        while ((inb(ioaddr + NE_EN0_ISR) & ENISR_RESET) == 0)
 189                if (jiffies - reset_start_time > 2*HZ/100) {
 190                        printk(" not found (no reset ack).\n");
 191                        return -ENODEV;
 192                }
 193
 194        outb(0xff, ioaddr + NE_EN0_ISR);                /* Ack all intr. */
 195    }
 196
 197#ifndef MANUAL_HWADDR0
 198
 199    /* Read the 16 bytes of station address PROM.
 200       We must first initialize registers, similar to NS8390_init(eifdev, 0).
 201       We can't reliably read the SAPROM address without this.
 202       (I learned the hard way!). */
 203    {
 204        struct {unsigned long value, offset; } program_seq[] = {
 205            {E8390_NODMA+E8390_PAGE0+E8390_STOP, NE_CMD}, /* Select page 0*/
 206            {0x48,      NE_EN0_DCFG},   /* Set byte-wide (0x48) access. */
 207            {0x00,      NE_EN0_RCNTLO}, /* Clear the count regs. */
 208            {0x00,      NE_EN0_RCNTHI},
 209            {0x00,      NE_EN0_IMR},    /* Mask completion irq. */
 210            {0xFF,      NE_EN0_ISR},
 211            {E8390_RXOFF, NE_EN0_RXCR}, /* 0x20  Set to monitor */
 212            {E8390_TXOFF, NE_EN0_TXCR}, /* 0x02  and loopback mode. */
 213            {32,        NE_EN0_RCNTLO},
 214            {0x00,      NE_EN0_RCNTHI},
 215            {0x00,      NE_EN0_RSARLO}, /* DMA starting at 0x0000. */
 216            {0x00,      NE_EN0_RSARHI},
 217            {E8390_RREAD+E8390_START, NE_CMD},
 218        };
 219        for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++) {
 220            outb(program_seq[i].value, ioaddr + program_seq[i].offset);
 221        }
 222
 223    }
 224    for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
 225        SA_prom[i] = inb(ioaddr + NE_DATAPORT);
 226        SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
 227        if (SA_prom[i] != SA_prom[i+1])
 228            wordlength = 1;
 229    }
 230
 231    /*  At this point, wordlength *only* tells us if the SA_prom is doubled
 232        up or not because some broken PCI cards don't respect the byte-wide
 233        request in program_seq above, and hence don't have doubled up values. 
 234        These broken cards would otherwise be detected as an ne1000.  */
 235
 236    if (wordlength == 2)
 237        for (i = 0; i < 16; i++)
 238                SA_prom[i] = SA_prom[i+i];
 239    
 240    if (wordlength == 2) {
 241        /* We must set the 8390 for word mode. */
 242        outb(0x49, ioaddr + NE_EN0_DCFG);
 243        start_page = NESM_START_PG;
 244        stop_page = NESM_STOP_PG;
 245    } else {
 246        start_page = NE1SM_START_PG;
 247        stop_page = NE1SM_STOP_PG;
 248    }
 249
 250    neX000 = (SA_prom[14] == 0x57  &&  SA_prom[15] == 0x57);
 251    ctron =  (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
 252
 253    /* Set up the rest of the parameters. */
 254    if (neX000) {
 255        name = (wordlength == 2) ? "NE2000" : "NE1000";
 256    } else if (ctron) {
 257        name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
 258        start_page = 0x01;
 259        stop_page = (wordlength == 2) ? 0x40 : 0x20;
 260    } else {
 261        printk(" not found.\n");
 262        return -ENXIO;
 263
 264    }
 265
 266#else
 267    wordlength = 2;
 268    /* We must set the 8390 for word mode. */
 269    outb(0x49, ioaddr + NE_EN0_DCFG);
 270    start_page = NESM_START_PG;
 271    stop_page = NESM_STOP_PG;
 272
 273    SA_prom[0] = MANUAL_HWADDR0;
 274    SA_prom[1] = MANUAL_HWADDR1;
 275    SA_prom[2] = MANUAL_HWADDR2;
 276    SA_prom[3] = MANUAL_HWADDR3;
 277    SA_prom[4] = MANUAL_HWADDR4;
 278    SA_prom[5] = MANUAL_HWADDR5;
 279    name = "NE2000";
 280#endif
 281
 282    dev->base_addr = ioaddr;
 283
 284    /* Install the Interrupt handler */
 285    i = request_irq(IRQ_AMIGA_PORTS, apne_interrupt, SA_SHIRQ, dev->name, dev);
 286    if (i) return i;
 287
 288    /* Allocate dev->priv and fill in 8390 specific dev fields. */
 289    if (ethdev_init(dev)) {
 290        printk (" unable to get memory for dev->priv.\n");
 291        free_irq(IRQ_AMIGA_PORTS, dev);
 292        return -ENOMEM;
 293    }
 294
 295    for(i = 0; i < ETHER_ADDR_LEN; i++) {
 296        printk(" %2.2x", SA_prom[i]);
 297        dev->dev_addr[i] = SA_prom[i];
 298    }
 299
 300    printk("\n%s: %s found.\n", dev->name, name);
 301
 302    ei_status.name = name;
 303    ei_status.tx_start_page = start_page;
 304    ei_status.stop_page = stop_page;
 305    ei_status.word16 = (wordlength == 2);
 306
 307    ei_status.rx_start_page = start_page + TX_PAGES;
 308
 309    ei_status.reset_8390 = &apne_reset_8390;
 310    ei_status.block_input = &apne_block_input;
 311    ei_status.block_output = &apne_block_output;
 312    ei_status.get_8390_hdr = &apne_get_8390_hdr;
 313    dev->open = &apne_open;
 314    dev->stop = &apne_close;
 315    NS8390_init(dev, 0);
 316
 317    pcmcia_ack_int(pcmcia_get_intreq());                /* ack PCMCIA int req */
 318    pcmcia_enable_irq();
 319
 320    apne_owned = 1;
 321
 322    return 0;
 323}
 324
 325static int
 326apne_open(struct net_device *dev)
 327{
 328    ei_open(dev);
 329    return 0;
 330}
 331
 332static int
 333apne_close(struct net_device *dev)
 334{
 335    if (ei_debug > 1)
 336        printk("%s: Shutting down ethercard.\n", dev->name);
 337    ei_close(dev);
 338    return 0;
 339}
 340
 341/* Hard reset the card.  This used to pause for the same period that a
 342   8390 reset command required, but that shouldn't be necessary. */
 343static void
 344apne_reset_8390(struct net_device *dev)
 345{
 346    unsigned long reset_start_time = jiffies;
 347
 348    init_pcmcia();
 349
 350    if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
 351
 352    outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
 353
 354    ei_status.txing = 0;
 355    ei_status.dmaing = 0;
 356
 357    /* This check _should_not_ be necessary, omit eventually. */
 358    while ((inb(NE_BASE+NE_EN0_ISR) & ENISR_RESET) == 0)
 359        if (jiffies - reset_start_time > 2*HZ/100) {
 360            printk("%s: ne_reset_8390() did not complete.\n", dev->name);
 361            break;
 362        }
 363    outb(ENISR_RESET, NE_BASE + NE_EN0_ISR);    /* Ack intr. */
 364}
 365
 366/* Grab the 8390 specific header. Similar to the block_input routine, but
 367   we don't need to be concerned with ring wrap as the header will be at
 368   the start of a page, so we optimize accordingly. */
 369
 370static void
 371apne_get_8390_hdr(struct net_device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
 372{
 373
 374    int nic_base = dev->base_addr;
 375    int cnt;
 376    char *ptrc;
 377    short *ptrs;
 378
 379    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
 380    if (ei_status.dmaing) {
 381        printk("%s: DMAing conflict in ne_get_8390_hdr "
 382           "[DMAstat:%d][irqlock:%d][intr:%d].\n",
 383           dev->name, ei_status.dmaing, ei_status.irqlock, dev->irq);
 384        return;
 385    }
 386
 387    ei_status.dmaing |= 0x01;
 388    outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
 389    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
 390    outb(sizeof(struct e8390_pkt_hdr), nic_base + NE_EN0_RCNTLO);
 391    outb(0, nic_base + NE_EN0_RCNTHI);
 392    outb(0, nic_base + NE_EN0_RSARLO);          /* On page boundary */
 393    outb(ring_page, nic_base + NE_EN0_RSARHI);
 394    outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
 395
 396    if (ei_status.word16) {
 397        ptrs = (short*)hdr;
 398        for(cnt = 0; cnt < (sizeof(struct e8390_pkt_hdr)>>1); cnt++)
 399            *ptrs++ = inw(NE_BASE + NE_DATAPORT);
 400    } else {
 401        ptrc = (char*)hdr;
 402        for(cnt = 0; cnt < sizeof(struct e8390_pkt_hdr); cnt++)
 403            *ptrc++ = inb(NE_BASE + NE_DATAPORT);
 404    }
 405
 406    outb(ENISR_RDC, nic_base + NE_EN0_ISR);     /* Ack intr. */
 407
 408    hdr->count = WORDSWAP(hdr->count);
 409
 410    ei_status.dmaing &= ~0x01;
 411}
 412
 413/* Block input and output, similar to the Crynwr packet driver.  If you
 414   are porting to a new ethercard, look at the packet driver source for hints.
 415   The NEx000 doesn't share the on-board packet memory -- you have to put
 416   the packet out through the "remote DMA" dataport using outb. */
 417
 418static void
 419apne_block_input(struct net_device *dev, int count, struct sk_buff *skb, int ring_offset)
 420{
 421    int nic_base = dev->base_addr;
 422    char *buf = skb->data;
 423    char *ptrc;
 424    short *ptrs;
 425    int cnt;
 426
 427    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
 428    if (ei_status.dmaing) {
 429        printk("%s: DMAing conflict in ne_block_input "
 430           "[DMAstat:%d][irqlock:%d][intr:%d].\n",
 431           dev->name, ei_status.dmaing, ei_status.irqlock, dev->irq);
 432        return;
 433    }
 434    ei_status.dmaing |= 0x01;
 435    outb(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
 436    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
 437    outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
 438    outb(count >> 8, nic_base + NE_EN0_RCNTHI);
 439    outb(ring_offset & 0xff, nic_base + NE_EN0_RSARLO);
 440    outb(ring_offset >> 8, nic_base + NE_EN0_RSARHI);
 441    outb(E8390_RREAD+E8390_START, nic_base + NE_CMD);
 442    if (ei_status.word16) {
 443      ptrs = (short*)buf;
 444      for (cnt = 0; cnt < (count>>1); cnt++)
 445        *ptrs++ = inw(NE_BASE + NE_DATAPORT);
 446      if (count & 0x01) {
 447        buf[count-1] = inb(NE_BASE + NE_DATAPORT);
 448      }
 449    } else {
 450      ptrc = (char*)buf;
 451      for (cnt = 0; cnt < count; cnt++)
 452        *ptrc++ = inb(NE_BASE + NE_DATAPORT);
 453    }
 454
 455    outb(ENISR_RDC, nic_base + NE_EN0_ISR);     /* Ack intr. */
 456    ei_status.dmaing &= ~0x01;
 457}
 458
 459static void
 460apne_block_output(struct net_device *dev, int count,
 461                const unsigned char *buf, const int start_page)
 462{
 463    int nic_base = NE_BASE;
 464    unsigned long dma_start;
 465    char *ptrc;
 466    short *ptrs;
 467    int cnt;
 468
 469    /* Round the count up for word writes.  Do we need to do this?
 470       What effect will an odd byte count have on the 8390?
 471       I should check someday. */
 472    if (ei_status.word16 && (count & 0x01))
 473      count++;
 474
 475    /* This *shouldn't* happen. If it does, it's the last thing you'll see */
 476    if (ei_status.dmaing) {
 477        printk("%s: DMAing conflict in ne_block_output."
 478           "[DMAstat:%d][irqlock:%d][intr:%d]\n",
 479           dev->name, ei_status.dmaing, ei_status.irqlock, dev->irq);
 480        return;
 481    }
 482    ei_status.dmaing |= 0x01;
 483    /* We should already be in page 0, but to be safe... */
 484    outb(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
 485
 486    outb(ENISR_RDC, nic_base + NE_EN0_ISR);
 487
 488   /* Now the normal output. */
 489    outb(count & 0xff, nic_base + NE_EN0_RCNTLO);
 490    outb(count >> 8,   nic_base + NE_EN0_RCNTHI);
 491    outb(0x00, nic_base + NE_EN0_RSARLO);
 492    outb(start_page, nic_base + NE_EN0_RSARHI);
 493
 494    outb(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
 495    if (ei_status.word16) {
 496        ptrs = (short*)buf;
 497        for (cnt = 0; cnt < count>>1; cnt++)
 498            outw(*ptrs++, NE_BASE+NE_DATAPORT);
 499    } else {
 500        ptrc = (char*)buf;
 501        for (cnt = 0; cnt < count; cnt++)
 502            outb(*ptrc++, NE_BASE + NE_DATAPORT);
 503    }
 504
 505    dma_start = jiffies;
 506
 507    while ((inb(NE_BASE + NE_EN0_ISR) & ENISR_RDC) == 0)
 508        if (jiffies - dma_start > 2*HZ/100) {           /* 20ms */
 509                printk("%s: timeout waiting for Tx RDC.\n", dev->name);
 510                apne_reset_8390(dev);
 511                NS8390_init(dev,1);
 512                break;
 513        }
 514
 515    outb(ENISR_RDC, nic_base + NE_EN0_ISR);     /* Ack intr. */
 516    ei_status.dmaing &= ~0x01;
 517    return;
 518}
 519
 520static void apne_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 521{
 522    unsigned char pcmcia_intreq;
 523
 524    if (!(gayle.inten & GAYLE_IRQ_IRQ))
 525        return;
 526
 527    pcmcia_intreq = pcmcia_get_intreq();
 528
 529    if (!(pcmcia_intreq & GAYLE_IRQ_IRQ)) {
 530        pcmcia_ack_int(pcmcia_intreq);
 531        return;
 532    }
 533    if (ei_debug > 3)
 534        printk("pcmcia intreq = %x\n", pcmcia_intreq);
 535    pcmcia_disable_irq();                       /* to get rid of the sti() within ei_interrupt */
 536    ei_interrupt(irq, dev_id, regs);
 537    pcmcia_ack_int(pcmcia_get_intreq());
 538    pcmcia_enable_irq();
 539}
 540
 541#ifdef MODULE
 542static struct net_device apne_dev;
 543
 544int init_module(void)
 545{
 546        int err;
 547
 548        apne_dev.init = apne_probe;
 549        if ((err = register_netdev(&apne_dev))) {
 550                if (err == -EIO)
 551                        printk("No PCMCIA NEx000 ethernet card found.\n");
 552                return (err);
 553        }
 554        return (0);
 555}
 556
 557void cleanup_module(void)
 558{
 559        unregister_netdev(&apne_dev);
 560
 561        pcmcia_disable_irq();
 562
 563        free_irq(IRQ_AMIGA_PORTS, &apne_dev);
 564
 565        pcmcia_reset();
 566
 567        apne_owned = 0;
 568}
 569
 570#endif
 571
 572static int init_pcmcia(void)
 573{
 574        u_char config;
 575#ifndef MANUAL_CONFIG
 576        u_char tuple[32];
 577        int offset_len;
 578#endif
 579        u_long offset;
 580
 581        pcmcia_reset();
 582        pcmcia_program_voltage(PCMCIA_0V);
 583        pcmcia_access_speed(PCMCIA_SPEED_250NS);
 584        pcmcia_write_enable();
 585
 586#ifdef MANUAL_CONFIG
 587        config = MANUAL_CONFIG;
 588#else
 589        /* get and write config byte to enable IO port */
 590
 591        if (pcmcia_copy_tuple(CISTPL_CFTABLE_ENTRY, tuple, 32) < 3)
 592                return 0;
 593
 594        config = tuple[2] & 0x3f;
 595#endif
 596#ifdef MANUAL_OFFSET
 597        offset = MANUAL_OFFSET;
 598#else
 599        if (pcmcia_copy_tuple(CISTPL_CONFIG, tuple, 32) < 6)
 600                return 0;
 601
 602        offset_len = (tuple[2] & 0x3) + 1;
 603        offset = 0;
 604        while(offset_len--) {
 605                offset = (offset << 8) | tuple[4+offset_len];
 606        }
 607#endif
 608
 609        out_8(GAYLE_ATTRIBUTE+offset, config);
 610
 611        return 1;
 612}
 613
 614MODULE_LICENSE("GPL");
 615
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.