patch-2.2.8 linux/drivers/usb/ohci-root-hub.c
Next file: linux/drivers/usb/ohci-root-hub.h
Previous file: linux/drivers/usb/ohci-hcd.h
Back to the patch index
Back to the overall index
- Lines: 605
- Date:
Mon May 10 10:18:34 1999
- Orig file:
v2.2.7/linux/drivers/usb/ohci-root-hub.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.2.7/linux/drivers/usb/ohci-root-hub.c linux/drivers/usb/ohci-root-hub.c
@@ -0,0 +1,604 @@
+/*
+ * HCD (OHCI) Virtual Root Hub for USB.
+ *
+ * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at)
+ *
+ * The Root Hub is build into the HC (UHCI or OHCI) hardware.
+ * This piece of code lets it look like it resides on the usb
+ * like the other hubs.
+ * (for anyone who wants to do a control operation on the root hub)
+ *
+ * v2.1 1999/05/09
+ * v2.0 1999/05/04
+ * v1.0 1999/04/27
+ * ohci-root-hub.c
+ *
+ */
+
+
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+
+#include "usb.h"
+#include "ohci-hcd.h"
+
+#ifdef VROOTHUB
+
+#include "ohci-root-hub.h"
+
+
+static __u8 root_hub_dev_des[] =
+{
+ 0x12, /* __u8 bLength; */
+ 0x01, /* __u8 bDescriptorType; Device */
+ 0x00, /* __u16 bcdUSB; v1.0 */
+ 0x01,
+ 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 bDeviceSubClass; */
+ 0x00, /* __u8 bDeviceProtocol; */
+ 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+ 0x00, /* __u16 idVendor; */
+ 0x00,
+ 0x00, /* __u16 idProduct; */
+ 0x00,
+ 0x00, /* __u16 bcdDevice; */
+ 0x00,
+ 0x00, /* __u8 iManufacturer; */
+ 0x00, /* __u8 iProduct; */
+ 0x00, /* __u8 iSerialNumber; */
+ 0x01 /* __u8 bNumConfigurations; */
+};
+
+
+/* Configuration descriptor */
+static __u8 root_hub_config_des[] =
+{
+ 0x09, /* __u8 bLength; */
+ 0x02, /* __u8 bDescriptorType; Configuration */
+ 0x19, /* __u16 wTotalLength; */
+ 0x00,
+ 0x01, /* __u8 bNumInterfaces; */
+ 0x01, /* __u8 bConfigurationValue; */
+ 0x00, /* __u8 iConfiguration; */
+ 0x40, /* __u8 bmAttributes;
+ Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */
+ 0x00, /* __u8 MaxPower; */
+
+ /* interface */
+ 0x09, /* __u8 if_bLength; */
+ 0x04, /* __u8 if_bDescriptorType; Interface */
+ 0x00, /* __u8 if_bInterfaceNumber; */
+ 0x00, /* __u8 if_bAlternateSetting; */
+ 0x01, /* __u8 if_bNumEndpoints; */
+ 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+ 0x00, /* __u8 if_bInterfaceSubClass; */
+ 0x00, /* __u8 if_bInterfaceProtocol; */
+ 0x00, /* __u8 if_iInterface; */
+
+ /* endpoint */
+ 0x07, /* __u8 ep_bLength; */
+ 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+ 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+ 0x03, /* __u8 ep_bmAttributes; Interrupt */
+ 0x40, /* __u16 ep_wMaxPacketSize; 64 Bytes */
+ 0x00,
+ 0xff /* __u8 ep_bInterval; 255 ms */
+};
+
+/*
+For OHCI we need just the 2nd Byte, so we
+don't need this constant byte-array
+
+static __u8 root_hub_hub_des[] =
+{
+ 0x00, * __u8 bLength; *
+ 0x29, * __u8 bDescriptorType; Hub-descriptor *
+ 0x02, * __u8 bNbrPorts; *
+ 0x00, * __u16 wHubCharacteristics; *
+ 0x00,
+ 0x01, * __u8 bPwrOn2pwrGood; 2ms *
+ 0x00, * __u8 bHubContrCurrent; 0 mA *
+ 0x00, * __u8 DeviceRemovable; *** 8 Ports max *** *
+ 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** *
+};
+*/
+
+
+int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int leni, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler)
+{
+
+ __u32 stat;
+ __u32 rep_handler;
+ int req_reply=0;
+ union ep_addr_ ep_addr;
+ union ep_addr_ ep_addr_ret;
+ __u8 * cmd = rh_cmd;
+ __u8 * data = rh_data;
+ int i;
+ int len =leni;
+
+ __u8 bmRequestType = cmd[0];
+ __u8 bRequest = cmd[1];
+ __u16 wValue = cmd[3] << 8 | cmd [2];
+ __u16 wIndex = cmd[5] << 8 | cmd [4];
+ __u16 wLength = cmd[7] << 8 | cmd [6];
+printk("USB root hub: adr: %8x cmd(%8x): ", ohci->root_hub_funct_addr, 8);
+for(i=0;i<8;i++)
+ printk("%2x", ((char *)rh_cmd)[i]);
+
+printk(" ; \n");
+
+ ep_addr_ret.iep = 0;
+ ep_addr_ret.bep.fa = ohci->root_hub_funct_addr;
+ ep_addr_ret.bep.ep = (bmRequestType & 0x80) | 0x40;
+
+ switch (bmRequestType | bRequest << 8) {
+ /* Request Destination:
+ without flags: Device,
+ RH_INTERFACE: interface,
+ RH_ENDPOINT: endpoint,
+ RH_CLASS means HUB here,
+ RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+ */
+
+ case RH_GET_STATUS:
+ len = 2;
+ data[0] = 0x01;
+ data[1] = 0x00;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_INTERFACE:
+ len = 2;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_ENDPOINT:
+ len = 2;
+ data[0] = 0x00;
+ data[1] = 0x00;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_CLASS: /* HUB_STATUS */
+ stat = readl(&ohci->regs->roothub.status) & 0x7fff7fff; /* bit 31 u. 15 has other meaning */
+ data[0] = stat & 0xff;
+ data[1] = (stat >> 8) & 0xff;
+ data[2] = (stat >> 16) & 0xff;
+ data[3] = (stat >> 24) & 0xff;
+ len = 4;
+ req_reply = RH_ACK;
+ break;
+ case RH_GET_STATUS | RH_OTHER | RH_CLASS: /* PORT_STATUS */
+ stat = readl(&ohci->regs->roothub.portstatus[wIndex-1]);
+ data[0] = stat & 0xff;
+ data[1] = (stat >> 8) & 0xff;
+ data[2] = (stat >> 16) & 0xff;
+ data[3] = (stat >> 24) & 0xff;
+ len = 4;
+ req_reply = RH_ACK;
+ printk("rh: stat %4x wIndex %4x;\n", stat , wIndex);
+ break;
+
+ case RH_CLEAR_FEATURE:
+ switch (wValue) {
+ case (RH_DEVICE_REMOTE_WAKEUP):
+ default:
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL):
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ default:
+ }
+ break;
+
+ case RH_CLEAR_FEATURE | RH_CLASS:
+ switch (wValue) {
+ /* case (RH_C_HUB_LOCAL_POWER): OHCI says: no switching of this one */
+ case (RH_C_HUB_OVER_CURRENT):
+ writel(RH_PS_OCIC, &ohci->regs->roothub.status);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ default:
+ }
+ break;
+ case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_ENABLE):
+ writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_SUSPEND):
+ writel(RH_PS_POCI, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_POWER):
+ writel(RH_PS_LSDA, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_CONNECTION):
+ writel(RH_PS_CSC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_ENABLE):
+ writel(RH_PS_PESC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_SUSPEND):
+ writel(RH_PS_PSSC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_OVER_CURRENT):
+ writel(RH_PS_OCIC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_C_PORT_RESET):
+ writel(RH_PS_PRSC, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ /*
+ case (RH_PORT_CONNECTION):
+ case (RH_PORT_OVER_CURRENT):
+ case (RH_PORT_RESET):
+ case (RH_PORT_LOW_SPEED):
+ */
+ default:
+ }
+ break;
+ case RH_SET_FEATURE:
+ switch (wValue) {
+ case (RH_DEVICE_REMOTE_WAKEUP):
+ default:
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_ENDPOINT:
+ switch (wValue) {
+ case (RH_ENDPOINT_STALL):
+ default:
+ }
+ break;
+
+ case RH_SET_FEATURE | RH_CLASS:
+ switch (wValue) {
+ /* case (RH_C_HUB_LOCAL_POWER): Root Hub has no local Power
+ case (RH_C_HUB_OVER_CURRENT): */
+ default:
+ }
+ break;
+ case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+ switch (wValue) {
+ case (RH_PORT_SUSPEND):
+ writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_RESET):
+ if((readl(&ohci->regs->roothub.portstatus[wIndex-1]) &1) != 0) /* BUG IN HUP CODE *********/
+ writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_POWER):
+ writel(RH_PS_PPS, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ case (RH_PORT_ENABLE):
+ writel(RH_PS_PES, &ohci->regs->roothub.portstatus[wIndex-1]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ /*
+ case (RH_PORT_CONNECTION):
+ case (RH_PORT_OVER_CURRENT):
+ case (RH_PORT_LOW_SPEED):
+ case (RH_C_PORT_CONNECTION):
+ case (RH_C_PORT_ENABLE):
+ case (RH_C_PORT_SUSPEND):
+ case (RH_C_PORT_OVER_CURRENT):
+ case (RH_C_PORT_RESET):
+ */
+ default:
+ }
+ break;
+
+ case RH_SET_ADDRESS:
+ ohci->root_hub_funct_addr = wValue;
+ /* ohci->ed_func_ep0[wValue] = &ohci->ed_rh_ep0;
+ ohci->ed_func_ep0[wValue]->ep_addr.bep.fa = wValue;
+ ohci->ed_func_ep0[wValue]->ed_list = &ohci->ed_rh_epi; */
+ ohci->ed_rh_epi.ed_list = NULL;
+ ohci->ed_rh_epi.ep_addr.bep.fa = wValue;
+ ohci->ed_rh_epi.ep_addr.bep.ep = 0xa1; /* Int in port 1 */
+ ohci->ed_func_ep0[0]= NULL;
+ len = 0;
+ req_reply = RH_ACK;
+ break;
+
+ case RH_GET_DESCRIPTOR:
+ switch ((wValue & 0xff00) >> 8) {
+ case (0x01): /* device descriptor */
+ len = min(sizeof(root_hub_dev_des), wLength);
+ memcpy(data, root_hub_dev_des, len);
+ req_reply = RH_ACK;
+ break;
+ case (0x02): /* configuration descriptor */
+ len = min(sizeof(root_hub_config_des), wLength);
+ memcpy(data, root_hub_config_des, len);
+ req_reply = RH_ACK;
+ break;
+ case (0x03): /* string descriptors */
+ default:
+ }
+ break;
+ case RH_GET_DESCRIPTOR | RH_CLASS:
+ data[1] = 0x29;
+ stat = readl(&ohci->regs->roothub.a);
+ data[2] = stat & 0xff; /* number of ports */
+ data[0] = (data[2] / 8) * 2 + 9; /* length of descriptor */
+ if(data[0] > wLength) {
+ req_reply = RH_REQ_ERR;
+ break;
+ }
+ data[3] = (stat >> 8) & 0xff;
+ data[4] = (stat >> 16) & 0xff;
+ data[5] = (stat >> 24) & 0xff;
+ data[6] = 0; /* Root Hub needs no current from bus */
+ stat = readl(&ohci->regs->roothub.b);
+ if(data[2] <= 8) { /* less than 8 Ports */
+ data[7] = stat & 0xff;
+ data[8] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1 ?, stat >> 16 for USB Rev. 1.0 */
+ }
+ else {
+ data[7] = stat & 0xff;
+ data[8] = (stat >> 8) & 0xff;
+ data[9] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 16 for USB Rev. 1.0 */
+ data[10] = (stat >> 24) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 24 for USB Rev. 1.0 */
+ }
+ len = data[0];
+ req_reply = RH_ACK;
+ break;
+
+ case RH_SET_DESCRIPTOR:
+ break;
+
+ case RH_GET_CONFIGURATION:
+ len = 1;
+ data[0] = 0x01;
+ req_reply = RH_ACK;
+ break;
+
+ case RH_SET_CONFIGURATION: /* start it up */
+ writel( 0x10000, &ohci->regs->roothub.status);
+ /*writel( OHCI_INTR_RHSC, &ohci->regs->intrenable);*/
+ len = 0;
+ req_reply = RH_ACK;
+ break;
+ /* Optional or meaningless requests
+ case RH_GET_STATE | RH_OTHER | RH_CLASS:
+ case RH_GET_INTERFACE | RH_INTERFACE:
+ case RH_SET_INTERFACE | RH_INTERFACE:
+ case RH_SYNC_FRAME | RH_ENDPOINT:
+ */
+
+ /* Vendor Requests, we are the vendor!
+ Will the USB-Consortium give us a Vendor Id
+ for a virtual hub-device :-) ?
+ We could use these requests for configuration purposes on the HCD Driver, not used in the altenate usb !*/
+
+ case RH_SET_FEATURE | RH_VENDOR: /* remove all endpoints of device wIndex = Dev << 8 */
+ switch(wValue) {
+ case RH_REMOVE_EP:
+ ep_addr.iep = 0;
+ ep_addr.bep.ep = wIndex & 0xff;
+ ep_addr.bep.fa = (wIndex << 8) & 0xff00;
+ usb_ohci_rm_function(ohci, ep_addr.iep);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ }
+ break;
+ case RH_SET_FEATURE | RH_ENDPOINT | RH_VENDOR: /* remove endpoint wIndex = Dev << 8 | EP */
+ switch(wValue) {
+ case RH_REMOVE_EP:
+ ep_addr.iep = 0;
+ ep_addr.bep.ep = wIndex & 0xff;
+ ep_addr.bep.fa = (wIndex << 8) & 0xff00;
+ usb_ohci_rm_ep(ohci, ohci_find_ep(ohci, ep_addr.iep));
+ len=0;
+ req_reply = RH_ACK;
+ break;
+ }
+ break;
+ case RH_SET_EP | RH_ENDPOINT | RH_VENDOR:
+ ep_addr.bep.ep = data[0];
+ ep_addr.bep.fa = data[1];
+ ep_addr.bep.hc = data[2];
+ ep_addr.bep.host = data[3];
+ rep_handler = data[7] << 24 |data[6] << 16 | data[5] << 8 | data[4];
+ /* struct usb_ohci_ed *usb_ohci_add_ep(union ep_addr_ ep_addr,
+ struct ohci * ohci, int interval, int load, int (*handler)(int, void*), int ep_size, int speed) */
+ usb_ohci_add_ep(ohci, ep_addr.iep, data[8], data[9], (f_handler) rep_handler, data[11] << 8 | data[10] , data[12]);
+ len=0;
+ req_reply = RH_ACK;
+ break;
+
+ default:
+ }
+ printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) ));
+ printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) ));
+
+ /* if (req_reply == RH_ACK) len; */
+ queue_reply(ohci, ep_addr_ret.iep, 8, rh_cmd, data, len, lw0, lw1, handler);
+ return 0;
+}
+
+int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler){
+
+ struct ohci_rep_td *td;
+ struct ohci_rep_td *tmp;
+ union ep_addr_ ep_addr;
+
+ td = kmalloc(sizeof(td),GFP_KERNEL);
+ tmp = ohci->td_rh_epi;
+ td->next_td = NULL;
+ if(tmp == NULL) { /* queue td */
+ ohci->td_rh_epi = td;
+ }
+ else {
+ while(tmp->next_td != NULL) tmp = tmp->next_td;
+ tmp->next_td = td;
+ }
+ ep_addr.iep = 0;
+ ep_addr.bep.fa = ohci->root_hub_funct_addr;
+ ep_addr.bep.ep = 0xA1; /* INT IN EP endpoint 1 */
+ td->cmd_len = 0;
+ td->cmd = NULL;
+ td->data = data;
+ td->data_len = data_len;
+ td->handler = handler;
+ td->next_td = NULL;
+ td->ep_addr = ep_addr.iep;
+ td->lw0 = lw0;
+ td->lw1 = lw1;
+ ohci_init_rh_int_timer(ohci, 255);
+ return 0;
+}
+
+
+/* prepare Interrupt pipe transaction data; HUP INTERRUPT ENDPOINT */
+int root_hub_send_irq(struct ohci * ohci, void * rh_data, int rh_len ) {
+
+ int num_ports;
+ int i;
+ int ret;
+ int len;
+
+ __u8 * data = rh_data;
+
+ num_ports = readl(&ohci->regs->roothub.a) & 0xff;
+ data[0] = (readl(&ohci->regs->roothub.status) & 0x00030000)>0?1:0;
+ ret = data[0];
+
+ for(i=0; i < num_ports; i++) {
+ data[i/8] |= ((readl(&ohci->regs->roothub.portstatus[i]) & 0x001f0000)>0?1:0) << ((i+1) % 8);
+ ret += data[i/8];
+ }
+ len = i/8 + 1;
+
+ if (ret > 0) return len;
+
+ return RH_NACK;
+}
+
+
+
+
+
+static struct timer_list rh_int_timer;
+
+/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */
+static void rh_int_timer_do(unsigned long ptr) {
+ int len;
+ int interval;
+ struct ohci * ohci = (struct ohci *) ptr;
+ struct ohci_rep_td *td = ohci->td_rh_epi;
+
+ if(td != NULL) { /* if ther is a TD handle the INT request */
+
+ len = root_hub_send_irq(ohci, td->data, td->data_len );
+ if(len > 0) {
+ ohci->td_rh_epi = td->next_td;
+ td->next_td = ohci->repl_queue;
+ ohci->repl_queue = td;
+ send_replies(ohci);
+ }
+ }
+ interval = ohci->rh_int_interval;
+ init_timer(& rh_int_timer);
+ rh_int_timer.function = rh_int_timer_do;
+ rh_int_timer.data = (unsigned long) ohci;
+ rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000;
+ add_timer(&rh_int_timer);
+}
+
+/* Root Hub INTs are polled by this timer */
+int ohci_init_rh_int_timer(struct ohci * ohci, int interval) {
+
+ if(!(ohci->rh_int_timer)) {
+ ohci->rh_int_timer = 1;
+ ohci->rh_int_interval = interval;
+ init_timer(& rh_int_timer);
+ rh_int_timer.function = rh_int_timer_do;
+ rh_int_timer.data = (unsigned long) ohci;
+ rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000;
+ add_timer(&rh_int_timer);
+ }
+ return 0;
+}
+
+int ohci_del_rh_int_timer(struct ohci * ohci) {
+ del_timer(&rh_int_timer);
+ return 0;
+}
+/* for root hub replies, queue the reply, (it will be sent immediately now) */
+
+int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler) {
+
+ struct ohci_rep_td *td;
+ int status = 0;
+
+printk("queue_reply ep: %x len: %x\n", ep_addr, len);
+td = kmalloc(sizeof(td), GFP_KERNEL);
+
+ if (len < 0) { status = len; len = 0;}
+ td->cmd_len = cmd_len;
+ td->cmd = cmd;
+ td->data = data;
+ td->data_len = len;
+ td->handler = handler;
+ td->next_td = ohci->repl_queue; ohci->repl_queue = td;
+ td->ep_addr = ep_addr;
+ td->lw0 = lw0;
+ td->lw1 = lw1;
+ td->status = status;
+ send_replies(ohci);
+return 0;
+}
+
+/* for root hub replies; send the reply */
+int send_replies(struct ohci * ohci) {
+ struct ohci_rep_td *td;
+ struct ohci_rep_td *tmp;
+
+ td = ohci->repl_queue; ohci->repl_queue = NULL;
+ while ( td != NULL) {
+ td->handler((void *) ohci, td->ep_addr,td->cmd_len,td->cmd, td->data, td->data_len, td->status, td->lw0, td->lw1);
+ tmp = td;
+ td = td->next_td;
+
+ kfree(tmp);
+ }
+ return 0;
+}
+
+#endif
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)