Back to home page

Quest Cross Reference

 
 

    


Warning, cross-references for /kernel/drivers/net/e1000e.c need to be fixed.

0001 /*                    The Quest Operating System
0002  *  Copyright (C) 2005-2010  Richard West, Boston University
0003  *
0004  *  This program is free software: you can redistribute it and/or modify
0005  *  it under the terms of the GNU General Public License as published by
0006  *  the Free Software Foundation, either version 3 of the License, or
0007  *  (at your option) any later version.
0008  *
0009  *  This program is distributed in the hope that it will be useful,
0010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0012  *  GNU General Public License for more details.
0013  *
0014  *  You should have received a copy of the GNU General Public License
0015  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
0016  */
0017 
0018 /* Intel e1000e NIC driver */
0019 
0020 #include "drivers/pci/pci.h"
0021 #include "drivers/net/ethernet.h"
0022 #include "arch/i386.h"
0023 #include "util/printf.h"
0024 #include "smp/smp.h"
0025 #include "smp/apic.h"
0026 #include "mem/physical.h"
0027 #include "mem/virtual.h"
0028 #include "kernel.h"
0029 
0030 #define DEBUG_E1000E
0031 
0032 #ifdef DEBUG_E1000E
0033 //#define DLOG(fmt,...) DLOG_PREFIX("e1000e",fmt,##__VA_ARGS__)
0034 #define DLOG(fmt,...) printf("e1000e: "fmt"\n",##__VA_ARGS__)
0035 #else
0036 #define DLOG(fmt,...) ;
0037 #endif
0038 
0039 #define E1000E_VECTOR 0x4D       /* arbitrary */
0040 
0041 #define RDESC_COUNT 8           /* must be multiple of 8 */
0042 #define RDESC_COUNT_MOD_MASK (RDESC_COUNT - 1)
0043 #define RBUF_SIZE   2048        /* configured in RCTL.BSIZE */
0044 #define RBUF_SIZE_MASK 0        /* 0 = 2048 bytes */
0045 
0046 #define TDESC_COUNT 8           /* must be multiple of 8 */
0047 #define TDESC_COUNT_MOD_MASK (RDESC_COUNT - 1)
0048 #define TBUF_SIZE   2048        /* configured in TCTL.BSIZE */
0049 #define TBUF_SIZE_MASK 0        /* 0 = 2048 bytes */
0050 #define TCTL_CT_MASK   0x100
0051 #define TCTL_COLD_MASK 0x40000
0052 #define TIPG_MASK (10 | (10 << 10) | (10 << 20))
0053 
0054 /* List of compatible cards (ended by { 0xFFFF, 0xFFFF }) */
0055 static struct { uint16 vendor, device; } compatible_ids[] = {
0056   { 0x8086, 0x10F5 },
0057   { 0xFFFF, 0xFFFF }
0058 };
0059 
0060 static uint8 hwaddr[ETH_ADDR_LEN];
0061 static uint device_index, mem_addr, flash_addr, irq_line, irq_pin, e1000e_phys;
0062 static volatile uint32 *mmio_base, *flash_base;
0063 #define E1000E_MMIO_PAGES 0x10
0064 
0065 /* ************************************************** */
0066 
0067 /* registers */
0068 
0069 #define REG(x) (mmio_base[x])
0070 
0071 #define CTRL   (REG (0x00))     /* Control */
0072 #define CTRL_RST (1<<26)        /* Reset */
0073 #define STATUS (REG (0x02))     /* Status */
0074 #define EERD   (REG (0x05))     /* EEPROM Read */
0075 #define ICR    (REG (0x30))     /* Interrupt Cause Read */
0076 #define ICR_RXT (0x80)          /* RX Timer Int. */
0077 #define ICR_RXO (0x40)          /* RX Overrun Int. */
0078 #define ICR_TXQE (0x02)         /* TX Queue Empty Int. */
0079 #define IMS    (REG (0x34))     /* Interrupt Mask Set */
0080 #define IMS_RXT (0x80)          /* RX Timer Int. */
0081 #define IMS_RXO (0x40)          /* RX Overrun Int. */
0082 #define IMS_TXQE (0x02)         /* TX Queue Empty Int. */
0083 #define IMC    (REG (0x36))     /* Interrupt Mask Clear */
0084 #define RCTL   (REG (0x40))     /* Receive Control */
0085 #define RCTL_EN (0x02)          /* RX Enable */
0086 #define RCTL_BAM (1<<15)        /* Accept Broadcast packets */
0087 #define RCTL_BSIZE (0x30000)    /* Buffer size */
0088 #define RCTL_BSEX (1<<25)       /* "Extension" (x16) of size */
0089 #define TCTL   (REG (0x100))    /* Transmit Control */
0090 #define TCTL_EN (0x02)          /* TX Enable */
0091 #define TCTL_PSP (0x08)         /* TX Pad Short Packets */
0092 #define TCTL_CT (0xFF0)         /* TX Collision Threshold */
0093 #define TCTL_COLD (0x3FF000)    /* TX Collision Distance */
0094 #define TIPG   (REG (0x104))    /* TX Inter Packet Gap */
0095 #define RDBAL  (REG (0xA00))    /* RX Desc. Base Address Low */
0096 #define RDBAH  (REG (0xA01))    /* RX Desc. Base Address High */
0097 #define RDLEN  (REG (0xA02))    /* RX Desc. Length */
0098 #define RDH    (REG (0xA04))    /* RX Desc Head */
0099 #define RDT    (REG (0xA06))    /* RX Desc Tail */
0100 #define TDBAL  (REG (0xE00))    /* TX Desc. Base Address Low */
0101 #define TDBAH  (REG (0xE01))    /* TX Desc. Base Address High */
0102 #define TDLEN  (REG (0xE02))    /* TX Desc. Length */
0103 #define TDH    (REG (0xE04))    /* TX Desc Head */
0104 #define TDT    (REG (0xE06))    /* TX Desc Tail */
0105 #define RAL    (REG (0x1500))   /* RX HW Address Low */
0106 #define RAH    (REG (0x1501))   /* RX HW Address High */
0107 
0108 /* ************************************************** */
0109 
0110 /* Receive descriptor (16-bytes) describes a buffer in memory */
0111 struct e1000e_rdesc {
0112   uint64 address;
0113   uint16 length;
0114   uint16 checksum;
0115   uint8  status;
0116   uint8  errors;
0117   uint16 special;
0118 } PACKED;
0119 #define RDESC_STATUS_DD  0x01    /* indicates hardware done with descriptor */
0120 #define RDESC_STATUS_EOP 0x02    /* indicates end of packet */
0121 
0122 /* ************************************************** */
0123 
0124 /* Transmit descriptor (16-bytes) describes a buffer in memory */
0125 struct e1000e_tdesc {
0126   uint64 address;
0127   uint16 length;
0128   uint8  cso;                   /* checksum offset */
0129   uint8  cmd;                   /* command */
0130   uint8  sta:4;                 /* status */
0131   uint8  rsv:4;                 /* reserved */
0132   uint8  css;                   /* checksum start */
0133   uint16 special;
0134 } PACKED;
0135 #define TDESC_STA_DD  0x01 /* indicates hardware done with descriptor */
0136 #define TDESC_CMD_EOP 0x01 /* indicates end of packet */
0137 #define TDESC_CMD_RS 0x08  /* requests status report */
0138 
0139 /* ************************************************** */
0140 
0141 static struct e1000e_interface {
0142   struct e1000e_rdesc rdescs[RDESC_COUNT] ALIGNED(0x10);
0143   struct e1000e_tdesc tdescs[TDESC_COUNT] ALIGNED(0x10);
0144   uint8 rbufs[RDESC_COUNT][RBUF_SIZE];
0145   uint8 tbufs[TDESC_COUNT][TBUF_SIZE];
0146   uint  rx_idx;                 /* current RX descriptor */
0147   uint  tx_cnt;                 /* number of pending TX descriptors */
0148 } *e1000e;
0149 
0150 /* Virtual-to-Physical */
0151 #define V2P(ty,p) ((ty)((((uint) (p)) - ((uint) e1000e))+e1000e_phys))
0152 /* Physical-to-Virtual */
0153 #define P2V(ty,p) ((ty)((((uint) (p)) - e1000e_phys)+((uint) e1000e)))
0154 
0155 static ethernet_device e1000e_ethdev;
0156 
0157 /* ************************************************** */
0158 
0159 extern bool
0160 e1000e_get_hwaddr (uint8 a[ETH_ADDR_LEN])
0161 {
0162   int i;
0163   for (i=0; i<ETH_ADDR_LEN; i++)
0164     a[i] = hwaddr[i];
0165   return TRUE;
0166 }
0167 
0168 extern sint
0169 e1000e_transmit (uint8* buffer, sint len)
0170 {
0171   uint32 tdt = TDT;
0172   DLOG ("TX: (%p, %d) TDH=%d TDT=%d", buffer, len, TDH, tdt);
0173 
0174   if (len > TBUF_SIZE)
0175     return 0;
0176 
0177   if (e1000e->tx_cnt >= TDESC_COUNT - 1) /* overrun */
0178     return 0;
0179 
0180   /* set up the first available descriptor */
0181   memcpy (e1000e->tbufs[tdt], buffer, len);
0182   e1000e->tdescs[tdt].length = len;
0183   e1000e->tdescs[tdt].cmd = TDESC_CMD_RS | TDESC_CMD_EOP;
0184 
0185   /* advance the TDT, notifying hardware */
0186   tdt++;
0187   if (tdt >= TDESC_COUNT)
0188     tdt = 0;
0189   TDT = tdt;
0190 
0191   e1000e->tx_cnt++;
0192 
0193   return len;
0194 }
0195 
0196 extern void
0197 e1000e_rx_poll (void)
0198 {
0199   uint32 entry, rdt;
0200   uint8 *ptr;
0201 
0202   DLOG ("RX: %d %d %d %d %d %d %d %d",
0203         e1000e->rdescs[0].status & RDESC_STATUS_DD,
0204         e1000e->rdescs[1].status & RDESC_STATUS_DD,
0205         e1000e->rdescs[2].status & RDESC_STATUS_DD,
0206         e1000e->rdescs[3].status & RDESC_STATUS_DD,
0207         e1000e->rdescs[4].status & RDESC_STATUS_DD,
0208         e1000e->rdescs[5].status & RDESC_STATUS_DD,
0209         e1000e->rdescs[6].status & RDESC_STATUS_DD,
0210         e1000e->rdescs[7].status & RDESC_STATUS_DD);
0211 
0212   entry = e1000e->rx_idx & RDESC_COUNT_MOD_MASK;
0213   while (e1000e->rdescs[entry].status & RDESC_STATUS_DD) {
0214     if (e1000e->rdescs[entry].status & RDESC_STATUS_EOP) {
0215       uint16 len;
0216       /* full packet */
0217       ptr = e1000e->rbufs[entry];
0218       len = e1000e->rdescs[entry].length;
0219       DLOG ("RX: full packet@%p len=%d", ptr, len);
0220       if (e1000e_ethdev.recv_func)
0221         e1000e_ethdev.recv_func (&e1000e_ethdev, ptr, len);
0222       else                      /* drop it */
0223         DLOG ("recv_func is null");
0224     } else {
0225       /* error */
0226       DLOG ("RX: error. status=%p", e1000e->rdescs[entry].status);
0227     }
0228 
0229     /* clear status */
0230     e1000e->rdescs[entry].status = 0;
0231 
0232     /* advance "tail" to notify hardware */
0233     rdt = RDT;
0234     rdt++;
0235     if (rdt >= RDESC_COUNT)
0236       rdt = 0;
0237     RDT = rdt;
0238 
0239     /* check next entry */
0240     entry = (++e1000e->rx_idx) & RDESC_COUNT_MOD_MASK;
0241   }
0242 }
0243 
0244 static void
0245 handle_tx (uint32 icr)
0246 {
0247   uint i;
0248   DLOG ("TX: tx_cnt=%d", e1000e->tx_cnt);
0249 
0250   /* find descriptors that have completed */
0251   for (i=0; i<TDESC_COUNT; i++) {
0252     if (e1000e->tdescs[i].cmd && (e1000e->tdescs[i].sta & TDESC_STA_DD)) {
0253       e1000e->tdescs[i].cmd = 0;
0254       e1000e->tdescs[i].sta = 0;
0255       e1000e->tx_cnt--;
0256     }
0257   }
0258 }
0259 
0260 extern void
0261 e1000e_poll (void)
0262 {
0263   e1000e_rx_poll ();
0264   handle_tx (ICR_TXQE);
0265 }
0266 
0267 static uint32
0268 e1000e_irq_handler (uint8 vec)
0269 {
0270   /* ICR is cleared upon read; this implicitly acknowledges the
0271    * interrupt. */
0272   uint32 icr = ICR;
0273   DLOG ("IRQ: ICR=%p", icr);
0274 
0275   if (icr & ICR_RXT)            /* RX */
0276     e1000e_rx_poll ();
0277   if (icr & ICR_TXQE)           /* TX queue empty */
0278     handle_tx (icr);
0279 
0280   return 0;
0281 }
0282 
0283 static void
0284 reset (void)
0285 {
0286   uint i;
0287 
0288   /* disable PCIe mastering */
0289   //DLOG ("Master Disable CTRL=%p", CTRL);
0290   CTRL |= 0x4;
0291 
0292   //DLOG ("Masking interrupts");
0293   IMC = (~0);
0294 
0295   //DLOG ("Disable TX and RX");
0296   RCTL = 0;
0297   TCTL = TCTL_PSP;
0298 
0299   //DLOG ("STATUS=%p RCTL=%p TCTL=%p", STATUS, RCTL, TCTL);
0300 
0301   STATUS &= ~(0x600);
0302 
0303   //DLOG ("cleared PHYRST and INIT_DONE; STATUS=%p", STATUS);
0304 
0305   /* get sw control */
0306   //DLOG ("(before) EXTCNF_CTRL=%p", REG(0x3C0));
0307   REG(0x3C0) |= 0x20;
0308   //DLOG ("(after) EXTCNF_CTRL=%p", REG(0x3C0));
0309 
0310   /* reset */
0311 
0312   //DLOG ("reseting CTRL=%p", CTRL);
0313   //CTRL |= (0x80000000 | CTRL_RST);
0314   //  while (CTRL & CTRL_RST) tsc_delay_usec (1);
0315   DLOG ("CTRL=%p", CTRL);
0316 
0317   /* set hardware address */
0318   RAL =
0319     (hwaddr[0] << 0x00) |
0320     (hwaddr[1] << 0x08) |
0321     (hwaddr[2] << 0x10) |
0322     (hwaddr[3] << 0x18);
0323   RAH = 0x80000000L     |       /* Address Valid */
0324     (hwaddr[4] << 0x00) |
0325     (hwaddr[5] << 0x08);
0326 
0327   DLOG ("RAL=%p RAH=%p", RAL, RAH);
0328 
0329   /* set rx buffer size code */
0330   RCTL &= ~RCTL_BSIZE;
0331   RCTL |= RBUF_SIZE_MASK;
0332   RCTL &= ~RCTL_BSEX;
0333 
0334   /* set up rdesc addresses */
0335   for (i=0; i<RDESC_COUNT; i++) {
0336     e1000e->rdescs[i].address = V2P (uint64, e1000e->rbufs[i]);
0337     e1000e->rdescs[i].status = 0;
0338   }
0339 
0340   /* program the rdesc base address and length */
0341   RDBAL = V2P (uint32, e1000e->rdescs);
0342   RDBAH = 0;
0343   RDLEN = RDESC_COUNT * sizeof (struct e1000e_rdesc);
0344 
0345   /* set head, tail of rx ring buffer */
0346   RDH = 0;
0347   RDT = RDESC_COUNT - 1;
0348 
0349   e1000e->rx_idx = 0;
0350 
0351   DLOG ("RDBAL=%p RDLEN=%p RDH=%d RDT=%d",
0352         V2P (uint32, e1000e->rdescs), RDESC_COUNT * sizeof (struct e1000e_rdesc),
0353         RDH, RDT);
0354 
0355   /* set up tdesc addresses */
0356   for (i=0; i<TDESC_COUNT; i++) {
0357     e1000e->tdescs[i].address = V2P (uint64, e1000e->tbufs[i]);
0358     e1000e->tdescs[i].sta = 0;
0359   }
0360 
0361   /* program the tdesc base address and length */
0362   TDBAL = V2P (uint32, e1000e->tdescs);
0363   TDBAH = 0;
0364   TDLEN = TDESC_COUNT * sizeof (struct e1000e_tdesc);
0365 
0366   /* set head, tail of rx ring buffer */
0367   TDH = 0;
0368   TDT = 0;
0369 
0370   e1000e->tx_cnt = 0;
0371 
0372   DLOG ("TDBAL=%p TDLEN=%p TDH=%d TDT=%d",
0373         V2P (uint32, e1000e->tdescs), TDESC_COUNT * sizeof (struct e1000e_tdesc),
0374         TDH, TDT);
0375 
0376   /* setup RX interrupts */
0377   IMS |= IMS_RXT;
0378 
0379   /* setup TX interrupts */
0380   IMS |= IMS_TXQE;
0381 
0382   /* enable RX operation and broadcast reception */
0383   RCTL |= (RCTL_EN | RCTL_BAM);
0384 
0385   /* enable TX operation */
0386   TCTL |= (TCTL_EN | TCTL_PSP | TCTL_COLD_MASK);
0387   TIPG = TIPG_MASK;
0388 
0389 
0390   /* re-enable mastering */
0391 
0392   CTRL &= (~0x4);
0393 
0394   /* relinquish SWFLAG */
0395   REG(0x3C0) &= ~(0x20);
0396 
0397   DLOG ("CTRL=%p STA=%p RCTL=%p TCTL=%p EXT=%p", CTRL, STATUS, RCTL, TCTL, REG(0x3C0));
0398 
0399   //while (! (TCTL & TCTL_EN)) asm volatile ("pause");
0400 }
0401 
0402 extern bool
0403 e1000e_init (void)
0404 {
0405   uint i, io_base, mask, frame_count;
0406 
0407   if (mp_ISA_PC) {
0408     DLOG ("Requires PCI support");
0409     goto abort;
0410   }
0411 
0412   for (i=0; compatible_ids[i].vendor != 0xFFFF; i++)
0413     if (pci_find_device (compatible_ids[i].vendor, compatible_ids[i].device,
0414                          0xFF, 0xFF, 0, &device_index))
0415       break;
0416     else
0417       device_index = ~0;
0418 
0419   if (device_index == (uint)(~0)) {
0420     DLOG ("Unable to detect compatible device.");
0421     goto abort;
0422   }
0423 
0424   DLOG ("Found device_index=%d", device_index);
0425 
0426   if (!pci_decode_bar (device_index, 0, &mem_addr, &io_base, &mask)) {
0427     DLOG ("Invalid PCI configuration or BAR0 not found");
0428     goto abort;
0429   }
0430 
0431   if (mem_addr == 0) {
0432     DLOG ("Unable to detect memory mapped IO region");
0433     goto abort;
0434   }
0435 
0436   /* Map some virtual pages to the memory-mapped I/O region */
0437   mmio_base = map_contiguous_virtual_pages (mem_addr | 3, E1000E_MMIO_PAGES);
0438 
0439   if (mmio_base == NULL) {
0440     DLOG ("Unable to map page to phys=%p", mem_addr);
0441     goto abort;
0442   }
0443 
0444   DLOG ("Using memory mapped IO at phys=%p virt=%p", mem_addr, mmio_base);
0445 
0446 
0447   /* 8257x */
0448   if (!pci_decode_bar (device_index, 1, &flash_addr, &io_base, &mask)) {
0449     DLOG ("Invalid PCI configuration or BAR1 not found");
0450     goto abort;
0451   }
0452 
0453   if (flash_addr == 0) {
0454     DLOG ("Unable to detect memory mapped flash region");
0455     goto no_flash;
0456   }
0457 
0458   /* Map some virtual pages to the memory-mapped flash region */
0459   flash_base = map_contiguous_virtual_pages (flash_addr | 3, 1);
0460 
0461   if (flash_base == NULL) {
0462     DLOG ("Unable to map page to phys=%p", flash_addr);
0463     goto abort;
0464   }
0465 
0466   DLOG ("Using memory mapped FLASH at phys=%p virt=%p", flash_addr, flash_base);
0467   DLOG ("flash=%p %p %p %p",
0468         flash_base[0], flash_base[1], flash_base[2], flash_base[3]);
0469  no_flash:
0470   /*  */
0471 
0472 
0473 
0474   /* I need contiguous physical and virtual memory for DMA memory */
0475   frame_count = sizeof (struct e1000e_interface) >> 12;
0476   if (sizeof (struct e1000e_interface) & 0xFFF)
0477     frame_count++;              /* round up */
0478 
0479   /* Obtain contiguous physical frames. */
0480   e1000e_phys = alloc_phys_frames (frame_count);
0481 
0482   if (e1000e_phys == -1) {
0483     DLOG ("Unable to allocate physical memory");
0484     goto abort_mmap;
0485   }
0486 
0487   /* Map contiguous virtual pages to contiguous physical frames. */
0488   e1000e = (struct e1000e_interface *)
0489     map_contiguous_virtual_pages (e1000e_phys | 3, frame_count);
0490 
0491   if (e1000e == NULL) {
0492     DLOG ("Unable to allocate virtual memory");
0493     goto abort_phys;
0494   }
0495 
0496   /* zero the acquired memory */
0497   memset (e1000e, 0, sizeof (struct e1000e_interface));
0498 
0499   DLOG ("DMA region at virt=%p phys=%p count=%d", e1000e, e1000e_phys, frame_count);
0500 
0501   if (!pci_get_interrupt (device_index, &irq_line, &irq_pin)) {
0502     DLOG ("Unable to get IRQ");
0503     goto abort_virt;
0504   }
0505 
0506   DLOG ("Using IRQ line=%.02X pin=%X", irq_line, irq_pin);
0507 
0508   /* Map IRQ to handler */
0509   IOAPIC_map_GSI (IRQ_to_GSI (mp_ISA_bus_id, irq_line),
0510                   E1000E_VECTOR, 0xFF00000000000800LL);
0511   set_vector_handler (E1000E_VECTOR, e1000e_irq_handler);
0512 
0513 #if 0
0514   /* read hardware individual address from EEPROM */
0515   EERD = 0x0001;                /* EERD.START=0 EERD.ADDR=0 */
0516   while (!(EERD & 0x10)) asm volatile ("pause"); /* wait for EERD.DONE */
0517   hwaddr[0] = (EERD >> 16) & 0xFF;
0518   hwaddr[1] = (EERD >> 24) & 0xFF;
0519   EERD = 0x0101;                /* EERD.START=0 EERD.ADDR=1 */
0520   while (!(EERD & 0x10)) asm volatile ("pause"); /* wait for EERD.DONE */
0521   hwaddr[2] = (EERD >> 16) & 0xFF;
0522   hwaddr[3] = (EERD >> 24) & 0xFF;
0523   EERD = 0x0201;                /* EERD.START=0 EERD.ADDR=2 */
0524   while (!(EERD & 0x10)) asm volatile ("pause"); /* wait for EERD.DONE */
0525   hwaddr[4] = (EERD >> 16) & 0xFF;
0526   hwaddr[5] = (EERD >> 24) & 0xFF;
0527 #endif
0528   for (i=0;i<6;i++) hwaddr[i] = i;
0529 
0530   DLOG ("hwaddr=%.02x:%.02x:%.02x:%.02x:%.02x:%.02x",
0531         hwaddr[0], hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5]);
0532 
0533   reset ();
0534 
0535   /* Register network device with net subsystem */
0536   e1000e_ethdev.recv_func = NULL;
0537   e1000e_ethdev.send_func = e1000e_transmit;
0538   e1000e_ethdev.get_hwaddr_func = e1000e_get_hwaddr;
0539   e1000e_ethdev.poll_func = e1000e_poll;
0540 
0541   if (!net_register_device (&e1000e_ethdev)) {
0542     DLOG ("registration failed");
0543     goto abort_virt;
0544   }
0545 
0546   return TRUE;
0547 
0548  abort_virt:
0549   unmap_virtual_pages (e1000e, frame_count);
0550  abort_phys:
0551   free_phys_frames (e1000e_phys, frame_count);
0552  abort_mmap:
0553   unmap_virtual_pages ((void *)mmio_base, E1000E_MMIO_PAGES);
0554  abort:
0555   return FALSE;
0556 }
0557 
0558 /* 
0559  * Local Variables:
0560  * indent-tabs-mode: nil
0561  * mode: C
0562  * c-file-style: "gnu"
0563  * c-basic-offset: 2
0564  * End: 
0565  */
0566 
0567 /* vi: set et sw=2 sts=2: */