/* -*- c-basic-offset: 2 -*- vim:noet:sw=2 */ /* Control program for the 'xdslusb' and 'cxacru' drivers. Copyright 2003 Josep Comas and contributors. http://WWW.AccessRunner.SourceForge.net http://WWW.sabi.co.UK/Proj/udsl/ This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #define GNU_SOURCE #include #include #include "usbi.h" #include "udsl.h" #ifndef DEBUG # define DEBUG 1 #endif #ifndef DEBUG_LOCALE # define DEBUG_LOCALE 0 #endif #ifndef DEBUG_TRANSFER # define DEBUG_TRANSFER 1 #endif #ifndef DEBUG_FW_UPLOAD # define DEBUG_FW_UPLOAD 1 #endif #ifndef SIMULATE # define SIMULATE 0 #endif #ifndef UNUSED # define UNUSED 0 #endif #if (DEBUG) # define PDEBUG(arg ...) if (opt_debug) fprintf(stderr,arg) #else # define PDEBUG(arg ...) #endif #define MODEM_FW_PREFIX "/usr/local/lib/cxacru/" static char *const locale_id = "udslctl"; static char *const locale_dir = "/usr/share/locale"; static char *const locale_dir_debug = "locale"; const char *const cmd_name = "udslctl"; static unsigned opt_verbose = 0; static unsigned opt_debug = 0; /* Defined in ANSI T1.413-1998 Annex D. */ static const char *adsl_vendors[] = { "not allocated", "not allocated", "Westell, Inc.", "ECI Telecom", "Texas Instruments", "Intel", "Amati Communcations Corp.", "General Data Communications, Inc.", "Level One Communications", "Crystal Semiconductor", "Lucent Technologies", "Aware, Inc.", "Brooktree", "NEC", "Samsung", "Northern Telecom, Inc.", "PairGain Technologies", "Paradyne", "Adtran", "INC", "ADC Telecommunications", "Motorola", "IBM Corp.", "Newbridge Network Corp.", "DSC", "Teltrend", "Exar Corp.", "Siemens Telecom Networks", "Analog Devices", "Nokia", "Ericsson Information Systems", "Tellabs Operations, Inc.", "Orckit Communications, Inc.", "AWA", "Alcatel Network Systems, Inc.", "National Semiconductor Corp.", "Italtel", "SAT - Société Anonyme de Télécommunications", "Fujitsu Network Trans. Systems", "MITEL", "Conklin Corp.", "Diamond Lane", "Cabletron Systems, Inc.", "Davicom Semiconductor, Inc.", "Metalink", "Pulsecom", "US Robotics", "AG Communications Systems", "Rockwell", "Harris", "Hayes Microcomputer Products, Inc.", "Co-optic", "Netspeed, Inc.", "3-Com", "Copper Mountain, Inc", "Silicon Automation Systems, Ltd", "Ascom", "Globespan Semiconductor, Inc.", "STMicroelectronics", "Coppercom", "Compaq Computer Corp.", "Integrated Technology Express", "Bay Networks, Inc.", "Next Level Communications", "Multi-Tech Systems, Inc.", "AMD", "Sumitomo Electric", "Philips M&N Systems", "Efficient Networks, Inc.", "Interspeed", "Cisco Systems", "Tollgrade Communications, Inc.", "Cayman Systems", "FlowPoint Corp.", "I.C.COM", "Matsushita", "Siemens Semiconductor", "Digital Link", "Digitel", "Alcatel Microelectronics", "Centillium Corp.", "Applied Digital Access, Inc.", "Smart Link, Ltd.", }; static unsigned adsl_vendors_count = sizeof adsl_vendors/sizeof *adsl_vendors; #define KIND_UNKNOWN 0 #define KIND_CXA_EUPHRATES 1 #define KIND_OLI_VER2 2 #define KIND_OLI_VER3 3 #define KIND_AMI_CA86U 4 #define KIND_CXA_HASBANI 5 #define KIND_ZOOM_5510A 6 /* This list should be in a file, preferably in a file exported by the driver itself under '/proc'. */ static const struct modem_id_kind { unsigned vid; unsigned pid; unsigned kind; } modem_id_kind_table[] = { /* Conexant: (Euphrates project) */ { 0x0572, 0xcafe, KIND_CXA_EUPHRATES }, /* Olitec: version 2 */ { 0x08e3, 0x0100, KIND_OLI_VER2 }, /* Olitec: version 3 */ { 0x08e3, 0x0102, KIND_OLI_VER3 }, /* Trust/Amigo Technology Co.: AMX-CA86U */ { 0x0eb0, 0x3457, KIND_AMI_CA86U }, /* Conexant: ADSL modem (Hasbani project) */ /* Amigo Technology Co: AMX-CA80U-2M */ /* E-Tech: ??? */ { 0x0572, 0xcb00, KIND_CXA_HASBANI }, /* ??? */ { 0x0572, 0xcb01, KIND_CXA_HASBANI }, /* Etec: USB ADSL Modem */ /* Aethra: Starmodem USB */ { 0x0572, 0xcb06, KIND_CXA_HASBANI }, /* Draytek: Vigor 318 */ { 0x0675, 0x0200, KIND_CXA_HASBANI }, /* Zoom: 5510A */ { 0x1803, 0x5510, KIND_ZOOM_5510A }, { 0x0000, 0x0000, 0 } }; /* Look up the USB ids and return a kind if the device is known, else 0. */ static unsigned modem_id_kind(const unsigned vid,const unsigned pid) { const struct modem_id_kind *p; for (p = &modem_id_kind_table[0]; p->kind != 0; p++) if (vid == p->vid && pid == p->pid) return p->kind; return 0; } /* Patch for the boot ROM. */ static const char unsigned modem_fw_patch[] = { 0x78, 0x20, 0x9f, 0xe5, 0x01, 0x10, 0xa0, 0xe3, /**/ 0x00, 0x10, 0x82, 0xe5, 0x70, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x00, 0x10, 0x82, 0xe5, 0x00, 0x10, 0xa0, 0xe3, 0x64, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x60, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x01, 0x10, 0xa0, 0xe3, 0x58, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, /**/ 0x00, 0x10, 0xa0, 0xe3, 0x50, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x4c, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x48, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x44, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x00, 0x10, 0xe0, 0xe3, 0x3c, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x38, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, /**/ 0x34, 0x20, 0x9f, 0xe5, 0x00, 0x10, 0x82, 0xe5, 0x30, 0x00, 0x9f, 0xe5, 0x00, 0xf0, 0xa0, 0xe1, 0x78, 0x00, 0x33, 0x00, 0x4c, 0x00, 0x35, 0x00, 0x3c, 0x00, 0x33, 0x00, 0x44, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x30, 0x00, 0x35, 0x00, 0x34, 0x00, 0x35, 0x00, 0x38, 0x00, 0x35, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x44, 0x00, 0x35, 0x00, /**/ 0x38, 0x00, 0x33, 0x00, 0x40, 0x00, 0x33, 0x00, 0x00, 0x10, 0x80, 0x00 }; /* modem internal characteristics */ struct usb_modem_desc { unsigned int vid; /* VendorID */ unsigned int pid; /* ProductID */ unsigned int arm_pll_f; /* FCLK PLL Register value */ unsigned int arm_pll_b; /* BCLK PLL Register value */ char patch_jump; /* 1 = patch, 2 = jump */ char *firmfile; /* firmware file name */ char param_first; /* param first offset */ char param_last; /* param last offset */ int params[0x50]; /* params */ }; /* info about modem */ struct usb_modem_info { char firm_version[5]; /* firmware version */ char mac[6]; /* MAC address */ int down_bitrate; /* download bitrate */ int up_bitrate; /* upload bitrate */ int link_status; /* link status */ int line_status; /* line status */ int operational_mode; /* operational mode */ }; static struct usb_modem_desc modem_desc; static struct usb_modem_info modem_info; static const char *const adsl_modes[] = { "cannot happen", "T1.413", "G.DMT", "G.Lite" }; /* Table of modem states, records the number of replies expected by issued commands. 0 (command not send/finished), 1 (wait 1 answer), 2 (wait 2 answers), ... */ static char unsigned modem_cmd_state[32]; static void fputcVis(const char unsigned c,FILE *const f) { fputc((c == 0) ? '_' : (isascii(c) && (c >= ' ' && c <= '~')) ? c : '.',f); } static void dump(FILE *const f, const char unsigned *const b, const unsigned n,const unsigned ln) { register unsigned i; for (i = 0; i < n; i += ln) { const unsigned e = i+ln; register unsigned j; for (j = i; j < n && j < e; j++) fprintf(f,"%02x ",b[j]); for (j = j; j < e; j++) fputs(" ",f); for (j = i; j < n && j < e; j++) fputcVis(b[j],f); fputc('\n',f); } } #if (UNUSED) /* Transfer a control message over the USB bus to the modem. */ static int transfer_ctrl_msg(usb_dev_handle *const adsl_handle, const unsigned requesttype,const unsigned request, const unsigned value,const unsigned index, const char unsigned *const data,const unsigned size) { int tmout = CTRL_TIMEOUT; int n = 0; unsigned i; for (i = 0; i < CTRL_MSG_RETRIES; i++) { #if (SIMULATE) n = size; #else n = usb_control_msg(adsl_handle, requesttype, request, value, index, (char *) data, size, tmout); #endif if (n >= 0) { #if (DEBUG_TRANSFER) if (opt_debug) { fprintf(stderr,"%s: transfer_ctrl_msg: bytes transferred: %d\n", __FUNCTION__,n); dump(stderr,data,n,16); } #endif break; } else { fprintf(stderr, gettext("%s: err: 'usb_control_msg' failed with error %s\n"), cmd_name,usb_strerror()); switch (-n) { case EPIPE: usb_clear_halt(adsl_handle,0x00); usb_clear_halt(adsl_handle,0x80); break; case ETIMEDOUT: tmout += TIMEOUT_ADD; break; } } } if (n < 0) { fprintf(stderr, gettext("%s: err: 'usb_control_msg' failed after %d retries\n"), cmd_name,CTRL_MSG_RETRIES); return -1; } return n; } #endif #if (UNUSED) /* Receive a packet from USB bus by bulk transfer. */ static int read_bulk(usb_dev_handle *const adsl_handle,int ep, char unsigned *const data,const unsigned data_sz) { int tmout = DATA_TIMEOUT; int n = 0; int i; memset(data,0,sizeof data); for (i = 0; i < READ_BULK_RETRIES; i++) { #if (SIMULATE) n = data_sz; #else n = usb_bulk_read(adsl_handle, ep, data, data_sz, tmout); #endif if (n >= 0) { #if (DEBUG_TRANSFER) if (opt_debug) { fprintf(stderr,"%s: bytes downloaded: %d\n",__FUNCTION__, n); dump(stderr,data,n,16); } #endif break; } else { fprintf(stderr, gettext("%s: err: 'usb_bulk_read' failed ('%s')\n"), cmd_name,usb_strerror()); switch (-n) { case EPIPE: usb_clear_halt(adsl_handle, ep); break; case ETIMEDOUT: tmout += TIMEOUT_ADD; break; } } } if (n < 0) { fprintf(stderr, gettext("%s: err: 'usb_bulk_read' failed after %d retries\n"), cmd_name,READ_BULK_RETRIES); return -1; } return 0; } #endif /* Send one or more packets on the USB bus to the modem by bulk transfer */ static int send_bulk(usb_dev_handle *const adsl_handle,const int ep, const char unsigned *const data, const unsigned packets,const unsigned packetsz) { int tmout = DATA_TIMEOUT; int n = 0; int i, j; PDEBUG("%s: ep %d data 0x%04lx packets %u packetsz %u\n", __FUNCTION__,ep,(long unsigned) data,packets,packetsz); for (i = 0; i < packets; i++) { for (j = 0; j < SEND_BULK_RETRIES; j++) { #if (SIMULATE) n = packetsz; #else n = usb_bulk_write(adsl_handle,ep, (char *) data+(i*packetsz),packetsz,tmout); #endif if (n >= 0) { #if (DEBUG_TRANSFER) if (opt_debug) { fprintf(stderr,"%s: %d bytes uploaded:\n",__FUNCTION__,n); if (1) dump(stderr,data+(i*packetsz),packetsz, 16); } #endif break; } else { fprintf(stderr, gettext("%s; err: 'usb_bulk_write' failed ('%s')\n"), cmd_name,usb_strerror()); switch (-n) { case EPIPE: usb_clear_halt(adsl_handle, ep); break; case ETIMEDOUT: tmout += TIMEOUT_ADD; break; } } } if (n < 0) { fprintf(stderr, gettext("%s: err: 'usb_bulk_write' failed after %d retries\n"), cmd_name,SEND_BULK_RETRIES); return -1; } } return 0; } static unsigned check_pending_cmd(const char unsigned *const s,const unsigned n) { int i; for (i = 0; i < n; i++) if (s[i] != 0) return 1; return 0; } /* Find a byte with a given id in a buffer and return the associated value. The buffer is a message, we assume it contains id/value pairs as given by the first byte of its second header word, and it contains in the data portion (after the first 8 bytes of header) pairs of 4-byte words, where the first byte of the first word is the value id, and the second word is the associated value */ static unsigned find_id_value(const char unsigned *const b, const unsigned id,unsigned *const value) { const unsigned n = b[4]; register unsigned i; for (i = 0; i < n; i++) { const char unsigned *const p = b + 8 + i*8; if (p[0] == id) { const char unsigned *const v = p + 4; *value = ((v[3] & 0xff) << 24) | ((v[2] & 0xff) << 16) | ((v[1] & 0xff) << 8) | (v[0] & 0xff); return 1; } } return 0; } static unsigned handle_cmd_answers(usb_dev_handle *const adsl_handle, const unsigned timeout) { char unsigned b[MAX_MESSAGE_SIZE]; time_t start,now; int n; int pending; (void) time(&start); do { pending = check_pending_cmd(modem_cmd_state,sizeof modem_cmd_state); if (pending) { memset(b,0,sizeof b); n = usb_bulk_read(adsl_handle,USB_IN_INFO, b,sizeof b,DATA_TIMEOUT); if (n < 0) { fprintf(stderr, gettext("%s: err: 'usb_bulk_read' failed (%s)\n"), cmd_name,usb_strerror()); switch (-n) { case EPIPE: usb_clear_halt(adsl_handle, USB_IN_INFO); break; case ETIMEDOUT: usb_resetep(adsl_handle, USB_IN_INFO); sleep(1); break; } } else if (n == sizeof b) { const unsigned cmd = b[0] & 0xff; #if (DEBUG_TRANSFER) if (opt_debug) { fprintf(stderr,"%s: USB message received, %d bytes:\n", __FUNCTION__,n); dump(stderr,b,n,16); } #endif if ((b[1] & 0xff) == REPLY_CMD) { switch (cmd) { case CMD_CARD_CONTROLLER_VERSION_GET: memcpy(modem_info.firm_version,b+4, sizeof modem_info.firm_version); break; case CMD_CARD_INFO_GET: { unsigned value; if (find_id_value(b,INFO_BITRATE_DOWN,&value)) modem_info.down_bitrate = value; if (find_id_value(b,INFO_BITRATE_UP,&value)) modem_info.up_bitrate = value; if (find_id_value(b,INFO_LINK_STATUS,&value)) modem_info.link_status = value; if (find_id_value(b,INFO_LINE_STATUS,&value)) modem_info.line_status = value; if (find_id_value(b,INFO_MAC_1TO4,&value)) { modem_info.mac[0] = (value >> 24) & 0xff; modem_info.mac[1] = (value >> 16) & 0xff; modem_info.mac[2] = (value >> 8) & 0xff; modem_info.mac[3] = (value >> 0) & 0xff; if (find_id_value(b,INFO_MAC_5TO6,&value)) { modem_info.mac[4] = (value >> 8) & 0xff; modem_info.mac[5] = (value >> 0) & 0xff; } } if (find_id_value(b,INFO_CURRENT_MODE,&value)) modem_info.operational_mode = value; } break; } if (cmd >= FIRST_CMD && cmd < (FIRST_CMD + sizeof modem_cmd_state) && modem_cmd_state[cmd-FIRST_CMD] > 0) modem_cmd_state[cmd-FIRST_CMD]--; } } } (void) time(&now); } while (pending && difftime(now,start) < timeout); if (pending != 0) { fprintf(stderr, gettext("%s: err: timeout (%ds) awaiting reply to USB command\n"), cmd_name,timeout); return 0; } return 1; } /* Send a bulk transfer and wait to receive N transfers */ static int send_cmd_wait_answers(usb_dev_handle *const adsl_handle, const char unsigned *const data, const unsigned initialwait, char unsigned answers, unsigned timeout) { if (adsl_handle == 0 || data == 0) return -1; { const char unsigned id = data[0]; unsigned wait = initialwait; unsigned i; for (i = 0; i < 4; i++) { modem_cmd_state[(id & 0xff)-FIRST_CMD] = 0; if (!send_bulk(adsl_handle, USB_OUT_INFO,data,1,MAX_MESSAGE_SIZE)) { modem_cmd_state[(id & 0xff)-FIRST_CMD] = answers; sleep(wait); if (handle_cmd_answers(adsl_handle,timeout)) return 0; } usb_resetep(adsl_handle, USB_IN_INFO); usb_resetep(adsl_handle, USB_OUT_INFO); wait += wait; } } return -1; } /* Send a command */ static int send_cmd_wait(usb_dev_handle *const adsl_handle, const unsigned id,const unsigned initialwait, const unsigned answers,const unsigned timeout) { char unsigned msg[MAX_MESSAGE_SIZE]; memset(msg,0,sizeof msg); msg[0] = id; return send_cmd_wait_answers(adsl_handle,msg,initialwait,answers,timeout); } /* Format a message */ static void usb_msg_hdr_set(const unsigned cmd,const unsigned length, const unsigned atype, const unsigned address, register char unsigned *const msg) { msg[0] = cmd & 0xff; /* usb command */ msg[1] = length & 0xff; /* msg length */ msg[2] = atype & 0xff; /* access type */ msg[3] = 0x00; /* ack request (0 = FALSE) */ msg[4] = (address >> 0) & 0xff; msg[5] = (address >> 8) & 0xff; msg[6] = (address >> 16) & 0xff; msg[7] = (address >> 24) & 0xff; } /* Write a value into an address */ static int write_value(usb_dev_handle *const adsl_handle, const unsigned address,const unsigned value) { char unsigned msg[MAX_MESSAGE_SIZE]; usb_msg_hdr_set(CMD_USB_WRITE_MEM,4,MA_DWORD,address,msg); msg[8] = (value >> 0) & 0xff; msg[9] = (value >> 8) & 0xff; msg[10] = (value >> 16) & 0xff; msg[11] = (value >> 24) & 0xff; memset(msg+12,0,sizeof msg-12); if (send_bulk(adsl_handle,USB_OUT_INFO,msg,1,sizeof msg) < 0) return -1; return 0; } #if (UNUSED) /* Send a command */ static int send_cmd(usb_dev_handle *const adsl_handle, const unsigned id,const unsigned answers) { char unsigned msg[MAX_MESSAGE_SIZE]; memset(msg,0,sizeof msg); msg[0] = id; modem_cmd_state[(id & 0xff)-FIRST_CMD] = 0; if (send_bulk(adsl_handle,USB_OUT_INFO,msg,1,MAX_MESSAGE_SIZE) < 0) return -1; modem_cmd_state[(id & 0xff)-FIRST_CMD] = answers; return 0; } #endif #if (UNUSED) /* Send a command with a value */ static int send_cmd_value(usb_dev_handle *const adsl_handle, const unsigned id,const unsigned answers, const unsigned value) { char unsigned msg[MAX_MESSAGE_SIZE]; memset(msg,0,sizeof msg); usb_msg_hdr_set(id,0,0,value,msg); modem_cmd_state[(id & 0xff)-FIRST_CMD] = 0; if (send_bulk(adsl_handle,USB_OUT_INFO,msg,1,MAX_MESSAGE_SIZE) < 0) return -1; modem_cmd_state[(id & 0xff)-FIRST_CMD] = answers; return 0; } #endif /* Send a goto command */ static int send_goto_cmd(usb_dev_handle *const adsl_handle, const unsigned address) { char unsigned msg[MAX_MESSAGE_SIZE]; memset(msg,0,sizeof msg); usb_msg_hdr_set(CMD_USB_GOTO_MEM,0,MA_BYTE,address,msg); if (send_bulk(adsl_handle,USB_OUT_INFO,msg,1,MAX_MESSAGE_SIZE) < 0) return -1; return 0; } #if 0 /* THIS IS ONE OF THE DUMBEST THINGS I HAVE SEEN IN QUITE A WHILE! */ /* Write open_mode into a file */ int write_configuration(char *filename, int open_mode) { FILE *params; /* file handle */ params = fopen(filename, "w"); if (params == NULL) { fprintf(stderr,gettext("Error: I can't create file %s\n"), filename); return -1; } if (fprintf(params, "PARAM_0a=%i\n", open_mode) < 0) { fprintf(stderr,gettext("Error: I can't write into file %s\n"), filename); return -1; } fclose(params); return 0; } #endif /* Send configuration. */ static int send_configuration(usb_dev_handle *const adsl_handle, const unsigned first,const unsigned last) { /* Each message can hold at most 7 variables. */ const unsigned nmax = 7; const unsigned nvars = last - first + 1; unsigned position = first; unsigned i; for (i = 0; i < nvars; i += 7) { const unsigned nvarsmsg = ((nvars-i) >= nmax) ? nmax : nvars-i; char unsigned msg[MAX_MESSAGE_SIZE]; unsigned j; memset(msg,0,sizeof msg); msg[0] = CMD_CARD_DATA_SET; msg[4] = nvarsmsg; for (j = 0; j < nvarsmsg; j++) { char unsigned *const p = msg + 8 + (j*8); char unsigned *const v = p + 4; p[0] = position; v[0] = (modem_desc.params[position & 0xff] >> 0) & 0xff; v[1] = (modem_desc.params[position & 0xff] >> 8) & 0xff; #if 0 /* THIS IS ONE OF THE DUMBEST THINGS I HAVE SEEN IN QUITE A WHILE! */ if (position == 0x0a) /* ADSL open mode */ if (write_configuration("/tmp/cxacru.params", modem_desc.params[position & 0xff])) return -1; #endif position++; } #if 0 if (send_cmd_wait_answers(adsl_handle,msg,2,ONE_ANSWER,10) < 0) return -1; #else if (send_bulk(adsl_handle,USB_OUT_INFO,msg,1,MAX_MESSAGE_SIZE) < 0) return -1; #endif } return 0; } /* Clear endpoints that we use. */ static void clear_endpoints(usb_dev_handle *const adsl_handle) { #if 1 usb_resetep(adsl_handle, USB_IN_INFO); usb_resetep(adsl_handle, USB_OUT_INFO); usb_resetep(adsl_handle, USB_IN_DATA); usb_resetep(adsl_handle, USB_OUT_DATA); #else usb_clear_halt(adsl_handle, USB_IN_INFO); usb_clear_halt(adsl_handle, USB_OUT_INFO); usb_clear_halt(adsl_handle, USB_IN_DATA); usb_clear_halt(adsl_handle, USB_OUT_DATA); #endif } /* Send_block */ /* We have a data block to send, but we must send it in messages, each message being at most MAX_MESSAGE_SIZE bytes, of which only MAX_DATA_MESSAGE_SIZE can be payload, and if smaller a message must be padded to be a multiple of 4; but we can send many messages in a batch, as long as the total size of the batch is less than MAX_DATA_TRANSFER. */ /* Classic producer-consumer situation, easy if we have two coroutines. What we do instead is repeated transfers of data between two queues, and we need to keep state as to where we are on each queue (the state of each coroutine). */ /* So we loop; on each iteration we check whether we have filled a batch, if so we send it and reset it to empty. Then we append a message header to the batch, append at most MAX_DATA_MESSAGE_SIZE bytes from the data block to it, some padding if needed, and continue. */ static const unsigned msg_data_sz_max = MAX_DATA_MESSAGE_SIZE; static const unsigned msg_hdr_sz = MAX_MESSAGE_SIZE-MAX_DATA_MESSAGE_SIZE; static const unsigned msg_sz_max = MAX_MESSAGE_SIZE; static const unsigned msg_sz_granule = 4; static int send_block(usb_dev_handle *const h,const unsigned address, const char unsigned *const d,const unsigned n) { /* Source queue, the data */ const char unsigned *const d_e = d + n; register const char unsigned *s; /* Target queue, the batch */ char unsigned b[MAX_DATA_TRANSFER]; char unsigned *const b_e = b + sizeof b; register char unsigned *t; assert (h != 0 && d != 0); assert ((msg_sz_max % msg_sz_granule) == 0); assert ((msg_data_sz_max % msg_sz_granule) == 0); /* We could allow a batch that is smaller than the largest packet size, as long as it was bigger than 'msg_hdr_sz+msg_sz_granule'... No. */ assert (sizeof b >= msg_sz_max); PDEBUG("%s: n %u sizeof b %u msg_sz_max %u msg_data\n", __FUNCTION__,n,sizeof b,msg_sz_max); for (s = d, t = b; s < d_e; s = s) { const unsigned d_done = s - d; const unsigned d_todo = d_e - s; const unsigned b_free = b_e - t; assert (d_done >= 0 && d_done < n); assert ((d_done+d_todo) == n); assert (b_free >= 0 && b_free <= sizeof b); /* Compute how many actual data bytes will go into the next message, and how many bytes the message needs to be long including any necessary padding. */ unsigned msg_queued_sz, msg_sz; if (d_todo >= msg_data_sz_max) { msg_queued_sz = msg_data_sz_max; msg_sz = msg_sz_max; } else { msg_queued_sz = d_todo; msg_sz = (msg_hdr_sz + msg_queued_sz)+msg_sz_granule-1; msg_sz /= msg_sz_granule; msg_sz *= msg_sz_granule; assert (msg_sz <= msg_sz_max); } PDEBUG("%s: d_done %4u d_todo %4u b_free %4u" "; msg_queued_sz %2u msg_sz %2u\n", __FUNCTION__,d_done,d_todo,b_free,msg_queued_sz,msg_sz); /* If the number of free remaining bytes in the batch is less than the size of the next message to be appended to it, empty the batch queue by sending its contents to the modem. */ if (msg_sz > b_free) { PDEBUG("%s: full: send %u bytes to address 0x%04x, %u done, %u to do.\n", __FUNCTION__,t-b,address+d_done-(sizeof b-b_free),d_done,d_todo); if (send_bulk(h,USB_OUT_INFO,b,1,t-b) < 0) return -1; t = b; } /* First append the header to the batch queue, then the data, then the padding if any. */ { const unsigned msg_data_sz = msg_sz - msg_hdr_sz; const unsigned msg_pad_sz = msg_data_sz - msg_queued_sz; usb_msg_hdr_set(CMD_USB_WRITE_MEM,msg_data_sz,MA_DWORD,address+d_done,t); t += msg_hdr_sz; memcpy(t,s,msg_queued_sz); t += msg_queued_sz; s += msg_queued_sz; assert (s <= d_e); if (msg_pad_sz != 0) { /* Zeroing is most likely unnecessary */ memset(t,0,msg_pad_sz); t += msg_pad_sz; } assert (t <= b_e); } } if (t > b) { PDEBUG("%s: last: send remaining %u bytes\n",__FUNCTION__,t-b); if (send_bulk(h,USB_OUT_INFO,b,1,t-b) < 0) return -1; t = b; } assert ((s-d) == n); assert ((t-b) == 0); return 0; } /* load firmware */ static int load_firmware(usb_dev_handle *const adsl_handle, const unsigned modem_kind,const char *const modem_fw) { FILE *fw; off_t fw_sz; time_t first, last, before; /* clear endpoints */ clear_endpoints(adsl_handle); /* load firmware */ if (opt_verbose) fprintf(stdout,gettext("%s: info: initializing (firmware file '%s')\n"), cmd_name,modem_fw); fw = fopen(modem_fw, "rb"); if (fw == NULL) { fprintf(stderr,gettext("%s: error: cannot open file '%s' (code %d)\n"), cmd_name,modem_fw,errno); return -1; } fseek(fw, 0L, SEEK_END); fw_sz = ftell(fw); #if 0 /* Check initial 5 bytes. */ { char unsigned b[5]; fw_sz_t n; fseek(fw,0L,SEEK_SET); n = fread(b,1,sizeof b,fw); if (n != 5) { fprintf(stderr, gettext("%s: error: can't read first 5 bytes from file '%s' (code %d)\n"), cmd_name,modem_fw,errno); return -1; } if (b[0] != FIRMBYTE1 || b[1] != FIRMBYTE2 || b[2] != FIRMBYTE3 || b[3] != FIRMBYTE4 || b[4] != FIRMBYTE5) { fprintf(stderr, gettext("%s: error: perhaps file '%s' is not firmware\n" " first 5 bytes: %0x02x:%0x02x:%0x02x:%0x02x:%0x02x\n" " instead of: %0x02x:%0x02x:%0x02x:%0x02x:%0x02x\n"), cmd_name,modem_fw, b[0],b[1],b[2],b[3],b[4], FIRMBYTE1,FIRMBYTE2,FIRMBYTE3,FIRMBYTE4,FIRMBYTE5); return -1; } } #endif /* Initialize clock and memory. */ if (opt_verbose) { fprintf(stderr,gettext("%s: info: setting clocks and memory:"), cmd_name); PDEBUG("%s\n"," (debugging)"); } if (write_value(adsl_handle, ARM_PLL_F, modem_desc.arm_pll_f) || write_value(adsl_handle, ARM_PLL_B, modem_desc.arm_pll_b) || write_value(adsl_handle, ARM_EMCR, SDRAM_ENABLE)) { if (opt_verbose) fputc('\n',stderr); fprintf(stderr,gettext("%s: error: cannot initialize clocks or memory\n"), cmd_name); return -1; } if (opt_verbose) fprintf(stderr," %s\n",gettext("done")); /* Send firmware */ if (opt_verbose) { fprintf(stderr,gettext("%s: info: uploading %luB from firmware file:"), cmd_name,(long unsigned) fw_sz); PDEBUG("%s\n"," (debugging)"); } fseek(fw,0L,SEEK_SET); { unsigned memory_address; char unsigned b[64*MAX_DATA_MESSAGE_SIZE]; size_t n; for (memory_address = FW_ADDRESS; (n = fread(b,1,64*MAX_DATA_MESSAGE_SIZE,fw)) > 0; memory_address += n) { #if (DEBUG_FW_UPLOAD) PDEBUG("%s: %ld bytes read from file, to be sent\n", __FUNCTION__,(long int) n); if (send_block(adsl_handle,memory_address,b,n)) return -1; #else const unsigned sav_debug = opt_debug; opt_debug = 0; if (send_block(adsl_handle,memory_address,b,n)) return -1; opt_debug = sav_debug; #endif } } fclose(fw); if (opt_verbose) fprintf(stderr," %s\n",gettext("done")); /* Post load */ PDEBUG("%s: post load actions:\n",__FUNCTION__); if (opt_verbose) { fprintf(stderr,gettext("%s: info: patching/starting firmware:"), cmd_name); PDEBUG("%s\n"," (debugging)"); } switch (modem_desc.patch_jump) { case CXPATCH: PDEBUG("%s: doing CXPATCH\n",__FUNCTION__); /* patch (boot rom) */ if (send_block(adsl_handle, BR_ADDRESS, modem_fw_patch, sizeof modem_fw_patch) < 0) return -1; /* write vendorID and productID (boot signature) */ if (write_value(adsl_handle, WB_SIGN_ADDRESS, (modem_desc.pid << 16) | modem_desc.vid) < 0) return -1; /* patch return address (boot rom) */ if (write_value(adsl_handle, BR_STACK_ADDRESS, BR_ADDRESS) < 0) return -1; break; case CXJUMP: PDEBUG("%s: doing CXJUMP\n",__FUNCTION__); /* write vendorID and productID (boot signature) */ if (write_value(adsl_handle, WB_SIGN_ADDRESS, (modem_desc.pid << 16) | modem_desc.vid) < 0) return -1; /* jump to firmware address */ if (send_goto_cmd(adsl_handle, FW_ADDRESS) < 0) return -1; break; } if (opt_verbose) fprintf(stderr," %s\n",gettext("done")); /* wait until firmware is ready */ sleep(1); /* Set parameters, get info, initialize ADSL line. */ /* Clear endpoints */ clear_endpoints(adsl_handle); sleep(1); /* Send configuration */ if (opt_verbose) { fprintf(stderr,gettext("%s: info: setting configuration:"),cmd_name); PDEBUG("%s\n"," (debugging)"); } PDEBUG("%s: sending CMD_CARD_DATA_SET\n",__FUNCTION__); if (send_configuration(adsl_handle, modem_desc.param_first,modem_desc.param_last) < 0) return -1; if (opt_verbose) fprintf(stderr," %s\n",gettext("done")); sleep(4); /* Start line. */ if (opt_verbose) { PDEBUG("%s: sending CMD_CHIP_ADSL_LINE_START\n",__FUNCTION__); fprintf(stderr,gettext("%s: info: starting the ADSL line:"),cmd_name); PDEBUG("%s\n"," (debugging)"); } if (send_cmd_wait(adsl_handle, CMD_CHIP_ADSL_LINE_START,1,ONE_ANSWER,5) < 0) return -1; if (opt_verbose) fprintf(stderr," %s\n",gettext("done")); #if (SIMULATE) exit(0); #endif /* waiting until line is up (a maximum time) */ if (opt_verbose) fprintf(stderr,gettext("%s: info: wait for ADSL line (timeout %us): "), cmd_name,MAX_WAIT_LINE_UP); time(&first); before = first; do { PDEBUG("%s: sending CMD_CARD_INFO_GET\n",__FUNCTION__); if (send_cmd_wait(adsl_handle, CMD_CARD_INFO_GET, 1, FOUR_ANSWERS, 5) < 0) return -1; PDEBUG("%s: link status = %02x\n",__FUNCTION__,modem_info.link_status); PDEBUG("%s: line status = %02x\n",__FUNCTION__,modem_info.line_status); if (difftime(time(&last), before) > 1) { fputc('.',stderr); before = last; } } while (modem_info.line_status != LINE_STATUS_UP && difftime(last, first) < MAX_WAIT_LINE_UP); putchar('\n'); if (opt_verbose) { if (modem_info.line_status != LINE_STATUS_UP) fprintf(stderr,gettext("%s: info: ADSL line down\n"),cmd_name); else { const unsigned mode = modem_info.operational_mode; fprintf(stderr,gettext("%s: info: ADSL line up" " (mode %u '%s', down %uKb/s, up %uKb/s)\n"), cmd_name, mode,(mode > 4) ? "unknown" : adsl_modes[mode], modem_info.down_bitrate, modem_info.up_bitrate); } } #if (0 && DEBUG) if (opt_debug) { fprintf(stderr,"%s: waiting to receive first ATM cells\n", __FUNCTION__); time(&first); before = first; do { char unsigned b[64*MAX_DATA_MESSAGE_SIZE]; const int n = usb_bulk_read(adsl_handle,USB_IN_DATA, b,sizeof b,DATA_TIMEOUT); if (n > 0) { fprintf(stderr,"%s: ATM cells received:\n",__FUNCTION__); dump(stderr,b,n,16); return 0; } if (difftime(time(&last), before) > 1) before = last; } while (difftime(last, first) < 60); } #endif return 0; } /* it inits modem according modem kind */ static void modem_desc_setup(const unsigned int modem_kind,const unsigned open_mode, const unsigned modem_vid,const unsigned modem_pid) { memset(&modem_desc, 0, sizeof(modem_desc)); if (open_mode != ADSL_OPEN_UNKNOWN) modem_desc.params[0x0a] = open_mode & 0xff; modem_desc.vid = modem_vid; modem_desc.pid = modem_pid; switch (modem_kind) { case KIND_CXA_EUPHRATES: modem_desc.arm_pll_f = PLL_FAST_144MHZ; modem_desc.arm_pll_b = PLL_FAST_100MHZ; modem_desc.patch_jump = CXPATCH; modem_desc.firmfile = MODEM_FW_PREFIX "cxfirm1.bin"; modem_desc.param_first = 0x00; modem_desc.param_last = 0x40; modem_desc.params[0x00] = 0x30; modem_desc.params[0x03] = 0x01; modem_desc.params[0x04] = 0x01; modem_desc.params[0x07] = 0x09; modem_desc.params[0x08] = 0x10; modem_desc.params[0x0a] = 0x03; modem_desc.params[0x0c] = 0x01; modem_desc.params[0x0d] = 0x02; modem_desc.params[0x0f] = 0xc8; modem_desc.params[0x10] = 0x01; modem_desc.params[0x12] = 0x39; modem_desc.params[0x14] = 0x01; modem_desc.params[0x17] = 0x0e; modem_desc.params[0x18] = 0x05; modem_desc.params[0x1b] = 0x0a; modem_desc.params[0x1c] = 0x01; modem_desc.params[0x1d] = 0x03; modem_desc.params[0x1e] = 0x0a; modem_desc.params[0x2f] = 0x14; modem_desc.params[0x30] = 0x0c; modem_desc.params[0x31] = 0x02; modem_desc.params[0x32] = 0x03; break; case KIND_OLI_VER2: modem_desc.arm_pll_f = PLL_FAST_144MHZ; modem_desc.arm_pll_b = PLL_FAST_100MHZ; modem_desc.patch_jump = CXPATCH; modem_desc.firmfile = MODEM_FW_PREFIX "cxfirm2.bin"; modem_desc.param_first = 0x00; modem_desc.param_last = 0x41; modem_desc.params[0x00] = 0x30; modem_desc.params[0x03] = 0x01; modem_desc.params[0x04] = 0x01; modem_desc.params[0x07] = 0x09; modem_desc.params[0x08] = 0x10; modem_desc.params[0x0c] = 0x01; modem_desc.params[0x0d] = 0x02; modem_desc.params[0x0f] = 0xc8; modem_desc.params[0x10] = 0x01; modem_desc.params[0x12] = 0x22; modem_desc.params[0x14] = 0x01; modem_desc.params[0x17] = 0x0e; modem_desc.params[0x18] = 0x05; modem_desc.params[0x2f] = 0x14; modem_desc.params[0x30] = 0x0c; modem_desc.params[0x31] = 0x02; modem_desc.params[0x32] = 0x03; break; case KIND_OLI_VER3: modem_desc.arm_pll_f = PLL_FAST_144MHZ_RM; modem_desc.arm_pll_b = PLL_FAST_100MHZ_RM; modem_desc.patch_jump = CXJUMP; modem_desc.firmfile = MODEM_FW_PREFIX "cxfirm3.bin"; modem_desc.param_first = 0x00; modem_desc.param_last = 0x48; modem_desc.params[0x03] = 0x01; modem_desc.params[0x04] = 0x01; modem_desc.params[0x07] = 0x09; modem_desc.params[0x08] = 0x10; modem_desc.params[0x0a] = 0x04; modem_desc.params[0x0c] = 0x01; modem_desc.params[0x0f] = 0xc8; modem_desc.params[0x10] = 0x01; modem_desc.params[0x14] = 0x01; modem_desc.params[0x17] = 0x0e; modem_desc.params[0x18] = 0x05; modem_desc.params[0x2d] = 0x80; modem_desc.params[0x2f] = 0x0a; modem_desc.params[0x30] = 0x0c; modem_desc.params[0x31] = 0x02; modem_desc.params[0x32] = 0x03; modem_desc.params[0x36] = 0x0180; break; case KIND_AMI_CA86U: modem_desc.arm_pll_f = PLL_FAST_144MHZ; modem_desc.arm_pll_b = PLL_FAST_100MHZ; modem_desc.patch_jump = CXPATCH; modem_desc.firmfile = MODEM_FW_PREFIX "cxfirm3.bin"; modem_desc.param_first = 0x00; modem_desc.param_last = 0x48; modem_desc.params[0x03] = 0x01; modem_desc.params[0x04] = 0x01; modem_desc.params[0x07] = 0x09; modem_desc.params[0x08] = 0x10; modem_desc.params[0x0d] = 0x02; modem_desc.params[0x0f] = 0xc8; modem_desc.params[0x10] = 0x01; modem_desc.params[0x12] = 0x22; modem_desc.params[0x14] = 0x01; modem_desc.params[0x17] = 0x0e; modem_desc.params[0x18] = 0x05; modem_desc.params[0x2d] = 0x80; modem_desc.params[0x2f] = 0x0a; modem_desc.params[0x30] = 0x0c; modem_desc.params[0x31] = 0x02; modem_desc.params[0x32] = 0x03; modem_desc.params[0x36] = 0x0180; break; case KIND_CXA_HASBANI: modem_desc.arm_pll_f = PLL_FAST_144MHZ_RM; modem_desc.arm_pll_b = PLL_FAST_100MHZ_RM; modem_desc.patch_jump = CXJUMP; modem_desc.firmfile = MODEM_FW_PREFIX "cxfirm4.bin"; modem_desc.param_first = 0x00; modem_desc.param_last = 0x4a; modem_desc.params[0x03] = 0x01; modem_desc.params[0x04] = 0x01; modem_desc.params[0x07] = 0x09; modem_desc.params[0x08] = 0x10; modem_desc.params[0x0c] = 0x01; modem_desc.params[0x0d] = 0x02; modem_desc.params[0x0f] = 0xc8; modem_desc.params[0x10] = 0x01; modem_desc.params[0x14] = 0x01; modem_desc.params[0x17] = 0x0e; modem_desc.params[0x18] = 0x05; modem_desc.params[0x19] = 0x04; modem_desc.params[0x2d] = 0x80; modem_desc.params[0x2f] = 0x0a; modem_desc.params[0x30] = 0x0c; modem_desc.params[0x31] = 0x02; modem_desc.params[0x32] = 0x03; modem_desc.params[0x36] = 0x0180; modem_desc.params[0x4a] = 0x1100; break; case KIND_ZOOM_5510A: modem_desc.arm_pll_f = PLL_FAST_144MHZ_RM; modem_desc.arm_pll_b = PLL_FAST_100MHZ_RM; modem_desc.patch_jump = CXJUMP; modem_desc.firmfile = MODEM_FW_PREFIX "cxfirm5.bin"; modem_desc.param_first = 0x00; modem_desc.param_last = 0x48; modem_desc.params[0x03] = 0x01; modem_desc.params[0x04] = 0x01; modem_desc.params[0x07] = 0x09; modem_desc.params[0x08] = 0x10; modem_desc.params[0x0c] = 0x01; modem_desc.params[0x0d] = 0x02; modem_desc.params[0x0f] = 0xc8; modem_desc.params[0x10] = 0x01; modem_desc.params[0x14] = 0x01; modem_desc.params[0x17] = 0x0e; modem_desc.params[0x18] = 0x05; modem_desc.params[0x2d] = 0x80; modem_desc.params[0x2f] = 0x0a; modem_desc.params[0x30] = 0x0c; modem_desc.params[0x31] = 0x02; modem_desc.params[0x32] = 0x03; modem_desc.params[0x36] = 0x0180; break; } } static void print_minfo(FILE *const f,const struct udsl_info *const mi) { /* Versions */ fprintf(f,gettext("driver version:\t\t%d.%d.%d (speed %s)\n"), mi->mod_major,mi->mod_minor,mi->mod_release, (mi->mod_speed == 1) ? gettext("max") : gettext("normal")); fprintf(f,gettext("hardware version:\t%u\n"),mi->hard_version); fputs(gettext("firmware version:\t"),stdout); { unsigned i; for (i = 0; i < sizeof mi->firm_version; i++) fputc(mi->firm_version[i],f); fputc('\n',f); } fprintf(f,gettext("MAC address:\t\t%02x:%02x:%02x:%02x:%02x:%02x\n"), mi->mac[0],mi->mac[1],mi->mac[2], mi->mac[3],mi->mac[4],mi->mac[5]); /* ATM configuration and statistics */ fprintf(f, "VPI.VCI:\t\t%d.%d\n", mi->vpi,mi->vci); fprintf(f,gettext("ATM cells:\t\tdown %5u up %5u\n"), mi->atm_cell_rx,mi->atm_cell_tx); fprintf(f,gettext("OAM loopback cells:\t%u\n"), mi->oam_loopback); /* ADSL status */ { const char *s; switch (mi->link_status) { case LINK_STATUS_NOT_CONNECTED: s = gettext("not established"); break; case LINK_STATUS_CONNECTED: s = gettext("connected"); break; case LINK_STATUS_LOST: s = gettext("lost"); break; default: s = gettext("unknown"); }; fprintf(f,gettext("link status:\t\t0x%04x '%s'\n"),mi->link_status,s); } { const char *s; switch (mi->line_status) { case LINE_STATUS_DOWN: s = gettext("down"); break; case LINE_STATUS_ATTEMPTING_TO_ACTIVATE: s = gettext("activating"); break; case LINE_STATUS_TRAINING: s = gettext("training"); break; case LINE_STATUS_CHANNEL_ANALYSIS: s = gettext("channel analysis"); break; case LINE_STATUS_UP: s = gettext("up"); break; case LINE_STATUS_WAITING: s = gettext("waiting"); break; case LINE_STATUS_INITIALIZING: s = gettext("initializing"); break; default: s = gettext("unknown"); }; fprintf(f,gettext("line status:\t\t0x%04x '%s'\n"), mi->line_status,s); } { const char *m; switch (mi->open_mode) { case ADSL_OPEN_AUTO1: m = gettext("auto-G.HS"); break; case ADSL_OPEN_AUTO2: m = gettext("auto-T1.413"); break; case ADSL_OPEN_HAND: m = gettext("G.HS"); break; case ADSL_OPEN_ANSI: m = gettext("T1.413"); break; case ADSL_OPEN_GDMT: m = gettext("G.DMT"); break; case ADSL_OPEN_GLITE: m = gettext("G.Lite"); break; default: m = gettext("unknown"); } fprintf(f,gettext("open mode:\t\t%s\n"),m); } { const char *m; switch (mi->operational_mode) { case ADSL_ANSI: m = gettext("T1.413"); break; case ADSL_GDMT: m = gettext("G.DMT"); break; case ADSL_GLITE: m = gettext("G.LITE"); break; default: m = gettext("unknown"); } fprintf(f,gettext("current mode:\t\t%s\n"),m); } #if 0 { const char *p; switch (mi->data_path) { case FastPath: p = gettext("fastpath"); break; case InterleavedPath: p = gettext("interleaved"); break; default: p = gettext("unknown"); } fprintf(f,gettext("data path:\t\t0x%02x '%s'\n"), mi->data_path,p); } #endif fprintf(f,gettext("startup attempts:\t%u\n"), mi->startup_attempts); fprintf(f,gettext("line startable:\t\t%u\n"), mi->line_startable); fprintf(f,gettext("link lost:\t\t%u\n"), mi->link_lost); fprintf(f,gettext("bitrates in Kb/s:\tdown %5u up %5u\n"), mi->down_bitrate, mi->up_bitrate); fprintf(f,gettext("ADSL DSLAM:\t\t0x%04x '%s'\n"), mi->adsl_headend, (mi->adsl_headend >= adsl_vendors_count) ? "unknown" : adsl_vendors[mi->adsl_headend]); fprintf(f,gettext("ADSL DSLAM env.:\t%u\n"), mi->adsl_headend_env); /* Extra data */ if (mi->extended_info == 1) { fprintf(f,gettext(" power in dBm/Hz:\t%-5d\n"), mi->transmitter_power-256); fprintf(f,gettext(" SNR margin in dB:\tdown %5.2f up %5.2f\n"), mi->down_snr_margin/256.0,mi->up_snr_margin/256.0); fprintf(f,gettext(" attenuation in dB:\tdown %5.2f up %5.2f\n"), mi->down_attenuation/512.0,mi->up_attenuation/512.0); fprintf(f,gettext(" bits per frame:\tdown %5d up %5d\n"), mi->down_bits_frame,mi->up_bits_frame); fprintf(f,gettext(" CRC errors:\t\tdown %5d up %5d\n"), mi->down_crc_errors,mi->up_crc_errors); fprintf(f,gettext(" FEC errors:\t\tdown %5d up %5d\n"), mi->down_fec_errors,mi->up_fec_errors); fprintf(f,gettext(" HEC errors:\t\tdown %5d up %5d\n"), mi->down_hec_errors,mi->up_hec_errors); } } static const char *const options = "hVvDlim:f:aAIsSxXz"; static const struct option options_long[] = { /*name has_arg,flag,val */ { "help", 0, 0, 'h' }, { "version", 0, 0, 'V' }, { "verbose", 0, 0, 'v' }, { "debug", 0, 0, 'D' }, { "list", 0, 0, 'l' }, { "initialize", 0, 0, 'i' }, { "mode", 1, 0, 'm' }, { "firmware", 1, 0, 'f' }, { "atm-on", 0, 0, 'a' }, { "atm-off", 0, 0, 'A' }, { "info", 0, 0, 'I' }, { "adsl-on", 0, 0, 's' }, { "adsl-off", 0, 0, 'S' }, { "extra-on", 0, 0, 'x' }, { "extra-off", 0, 0, 'X' }, { "zero", 0, 0, 'z' }, {0, 0, 0, 0 } }; static const char *const (help[]) = { "%s: -[hV]" "%s: [-v] [-m M] [-f F] -i [modemno]\n" "%s: [-v] -[ledIsSnNz] [modemno]\n" "Possible options:\n" " -h,--help this help\n", "\n" " -v,--verbose perform commands verbosely\n", "\n" " -l,--list list supported modems\n", "\n" " -i,--initialize initialize modem\n", " -m,--mode M initialize modem with ADSL mode M\n" " -f,--firmware F use firmware file F to initialize modem\n" "\n" " -a,--atm-on enable ATM connections\n", " -A,--atm-off disable ATM connections\n", " -I,--info info about modem\n", " -s,--adsl-on start ADSL line\n", " -S,--adsl-off stop ADSL line\n", " -x,--extra-on start collecting extra info (only if normal speed)\n", " -X,--extra-off stop collecting extra info\n", " -z,--zero clear counters\n", "Line mode M can be one of:\n" " \"auto-T1.413\", \"auto-G.HS\"" " \"T1.413\", \"G.HS\", \"G.DMT\", \"G.Lite\"\n", 0 }; static void print_help(FILE *const f,const char *const n) { const char *const *p; for (p = &(help[1]); *p != 0; p++) fprintf(f,*p,cmd_name); } #define DO_UNKNOWN 0 #define DO_LIST 1 #define DO_INIT 2 #define DO_IOCTL 3 static void print_version(FILE *const f,const char *const n) { fprintf(f,"%s:\n",n); fputs(gettext(" udsl driver(s) control program\n"),f); fputs(" 2004-01-01 Josep Comas \n",f); fputs(gettext(" See credits in documentation\n"),f); } extern int main(const int argc,char *const *argv) { unsigned modem_vid,modem_pid; unsigned modem_no; unsigned modem_kind; const char *modem_fw = 0; /* defaults */ unsigned modem_cmd = DO_LIST; unsigned modem_mode = ADSL_OPEN_AUTO1; unsigned modem_ioctl = USBXDSL_CMD_INFO; /* Locale setup */ { #if (DEBUG_LOCALE) const char *locale_dirp = access(locale_dir_debug,R_OK) ? locale_dir_debug : locale_dir; #else const char *locale_dirp = locale_dir; #endif bindtextdomain(locale_id,locale_dirp); textdomain(locale_id); setlocale(LC_ALL, ""); } #if 0 cmd_name = argv[0]; #endif /* Option processing */ { int opt,opt_no = 0; while ((opt = getopt_long(argc,argv, options,options_long,&opt_no)) != -1) { const char *const opt_name = options_long[opt_no].name; switch (opt) { case 'V': print_version(stdout,cmd_name); return 0; break; case 'h': print_help(stdout,cmd_name); return 0; break; case 'v': opt_verbose = 1; break; case 'D': opt_debug = 1; break; case 'l': modem_cmd = DO_LIST; break; case 'i': modem_cmd = DO_INIT; break; case 'm': modem_mode = (strcmp(optarg,"auto-G.HS") == 0) ? ADSL_OPEN_AUTO1 : (strcmp(optarg,"auto-T1.413") == 0) ? ADSL_OPEN_AUTO2 : (strcmp(optarg,"G.HS") == 0) ? ADSL_OPEN_HAND : (strcmp(optarg,"T1.413") == 0) ? ADSL_OPEN_ANSI : (strcmp(optarg,"G.DMT") == 0) ? ADSL_OPEN_GDMT : (strcmp(optarg,"G.Lite") == 0) ? ADSL_OPEN_GLITE : ADSL_OPEN_UNKNOWN; if (modem_mode == ADSL_OPEN_UNKNOWN) { fprintf(stderr, gettext("%s: invalid line initialization mode '%s'\n"), cmd_name,optarg); return -1; } break; case 'f': modem_fw = optarg; if (access(modem_fw,R_OK) < 0) { fprintf(stderr, gettext("%s: cannot read firmware file '%s' (%d)\n"), cmd_name,optarg,errno); return -1; } break; case 'a': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_START; break; case 'A': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_STOP; break; case 'I': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_INFO; break; case 's': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_ADSL_LINE_START; break; case 'S': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_ADSL_LINE_STOP; break; case 'x': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_START_EXTENDED_INFO; break; case 'X': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_STOP_EXTENDED_INFO; break; case 'z': modem_cmd= DO_IOCTL; modem_ioctl = USBXDSL_CMD_CLEAR_COUNTERS; break; case 0: /* Long option with zero 'flag' field. Should not happen here. */ assert (!"udslctl: all long options should have a short option"); break; case ':': /* Option with value does not have value. Should not happen here. */ fprintf(stderr,"%s: option '%c' ('%s') missing value\n", cmd_name,opt,opt_name); assert (!"udslctl: A options should have a short option"); return -1; case '?': /* Unrecognized option */ return -1; } } } /* The one single (optional) argument to this command is the modem number. */ modem_no = (optind == argc) ? 0 : atoi(argv[optind++]); /* Check no more arguments. */ if (optind != argc) { fprintf(stderr,gettext("%s: '%s' (and following) are invalid arguments\n"), cmd_name,argv[optind]); return -1; } /* init USB bus and find devices */ usb_init(); if (usb_find_busses() < 0) { fprintf(stderr,gettext("%s: cannot find USB busses\n"),cmd_name); return -1; } if (usb_find_devices() < 0) { fprintf(stderr,gettext("%s: cannot find USB devices\n"),cmd_name); return -1; } if (modem_cmd == DO_LIST) { const struct usb_bus *bus; const struct usb_device *dev; unsigned i = 0; for (bus = usb_get_busses(); bus != 0; bus = bus->next) for (dev = bus->devices; dev != 0; dev = dev->next) if (modem_id_kind(dev->descriptor.idVendor, dev->descriptor.idProduct) != 0) { modem_vid = dev->descriptor.idVendor; modem_pid = dev->descriptor.idProduct; fprintf(stdout,gettext("modem %u 0x%04x:0x%04x\n"), i,modem_vid,modem_pid); i++; } } else { struct usb_dev_handle *handle; const struct usb_bus *bus; struct usb_device *dev; struct usb_device *found = 0; unsigned i = 0; for (bus = usb_get_busses(); bus != 0 && !found; bus = bus->next) for (dev = bus->devices; dev != 0 && !found; dev = dev->next) if ((modem_kind = modem_id_kind(dev->descriptor.idVendor, dev->descriptor.idProduct)) != 0) if (i++ == modem_no) found = dev; if (found == 0) { fprintf(stderr,gettext("%s: err: supported modem %u not found\n"), cmd_name,modem_no); return -1; } modem_vid = found->descriptor.idVendor; modem_pid = found->descriptor.idProduct; if (opt_verbose) fprintf(stdout,gettext("%s: info: found" " supported modem %u 0x%04x:0x%04x\n"), cmd_name,modem_no,modem_vid,modem_pid); #if 0 PDEBUG(" bLength: 0x%02x\n", found->config->bLength); PDEBUG(" bDescriptorType: 0x%02x\n", found->config->bDescriptorType); PDEBUG(" wTotalLength: 0x%04x\n", found->config->wTotalLength); PDEBUG(" bNumInterfaces: 0x%02x\n", found->config->bNumInterfaces); PDEBUG(" bConfigurationValue: 0x%02x\n", found->config->bConfigurationValue); PDEBUG(" iConfiguration: 0x%02x\n", found->config->iConfiguration); PDEBUG(" bmAttributes: 0x%02x\n", found->config->bmAttributes); PDEBUG(" MaxPower: 0x%02x\n", found->config->MaxPower); #endif if ((handle = usb_open(found)) == 0) { fprintf(stderr,gettext("%s: err: could not get USB handle for" " supported modem %u 0x%04x:0x%04x\n"), cmd_name,modem_no,modem_vid,modem_pid); return -1; } if (usb_set_configuration(handle,1) < 0) { fprintf(stderr,gettext("%s: err: '%s' in 'usb_set_configuration()' for" " supported modem %u 0x%04x:0x%04x\n"), cmd_name,usb_strerror(),modem_no,modem_vid,modem_pid); usb_close(handle); return -1; } if (modem_cmd == DO_INIT) { int code; if (usb_claim_interface(handle, 0) < 0) { fprintf(stderr, gettext("%s: cannot claim USB interface '%s'\n"), cmd_name,usb_strerror()); return -1; } modem_desc_setup(modem_kind, modem_mode, modem_vid, modem_pid); if (modem_fw == 0) modem_fw = modem_desc.firmfile; code = load_firmware(handle, modem_kind, modem_fw); usb_release_interface(handle,0); if (code < 0) { usb_close(handle); return -1; } } else if (modem_cmd == DO_IOCTL) { struct udsl_req ioctl_req; struct udsl_info ioctl_info; ioctl_req.ifno = 0; ioctl_req.command = modem_ioctl; ioctl_req.data = (modem_ioctl == USBXDSL_CMD_INFO) ? &ioctl_info : 0; PDEBUG("%s: ioctl(%d,0x%04x,[%u,0x%04x,0x%04lx])\n", __FUNCTION__,handle->fd,USBDEVFS_IOCTL, ioctl_req.ifno,ioctl_req.command,(long unsigned) ioctl_req.data); { const int r = ioctl(handle->fd,USBDEVFS_IOCTL,&ioctl_req); if (r < 0) { fprintf(stderr, gettext("%s: err: failed ioctl 0x%04x err code %d for" " supported modem %u 0x%04x:0x%04x\n"), cmd_name,ioctl_req.command,r, modem_no,modem_vid,modem_pid); usb_close(handle); return -1; } if (modem_ioctl == USBXDSL_CMD_INFO) print_minfo(stdout,&ioctl_info); } } usb_close(handle); } return 0; } /* Author : Josep Comas Creation : 24/1/2003 Description: This program inits Conexant AccessRunner (USB ADSL Modem). Modems: User: Josep Comas Manufacturer: Mac System (http://www.macsysco.com/) Model: MA08CU (ADSL 100U modem) Distribuited by: Vitelcom Model: EPS 5002 USB Internet provider: Telefonica Country: Spain Protocols: RFC1483/2684 routed, PPPoE User: Pascal Boucher Manufacturer: Olitec (http://www.olitec.com/) Model: Modem USB ADSL V3 Internet provider: Tiscali Country: France Protocol: PPPoATM User: Thomas Mikosch Manufacturer: Amigo Technology Co. (http://www.amigo.com.tw) Model: AMX-CA86U Distribuited by: Trust (http://www.trust.com) Model: 235A Speedlink ADSL Web Modem (item no. 13141) Internet provider: HetNet Country: The Netherlands Protocol: PPPoATM User: Pawel Konieczny Manufacturer: Amigo Technology Co. (http://www.amigo.com.tw) Model: AMX-CA80U-4 Distributed by (OEM): E-Tech (http://www.e-tech.nu/zon) Model: E-Tech ADSL USB Modem V2 Internet provider: Zon (http://www.zonnet.nl) ADSL provider: Versatel (http://www.versatel.nl) Country: The Netherlands Protocol: RFC1483/2684 bridged/routed Log: 28/4/2003 Josep Comas Added Olitec Modem USB ADSL V3 changes from Pascal Boucher Added support for Amigo AMX-CA86U 30/4/2003 Josep Comas Added support for Olitec Modem USB ADSL V2 1/5/2003 Josep Comas Added dispatch routine to retrieve info for commands sent Retrieve firmware version Retrieve MAC address Retrieve line and ADSL status Retrieve downbitrate and upbitrate 2/5/2003 Josep Comas Better retrieving info (sleeps added) 5/5/2003 Josep Comas Update to work with libusb-0.1.7 3/6/2003 Josep Comas Added support for Amigo AMX-CA80U-2M Added some changes from Pawel Konieczny Added ADSL open mode Send commands in background and wait to finish 4/6/2003 Josep Comas Fix usb_bulk_msg timeout Adjust for speed machines (2.4GHz processor) 6/6/2003 Josep Comas Minor fixes for timeouts 7/6/2003 Josep Comas Retries for send and wait commands 13/6/2003 Josep Comas Support for all modems in same executable Return to old handle_cmd_answers process 16/6/2003 Josep Comas Fix getting info for Debian 21/6/2003 Josep Comas Increased timeout for some usb controllers/modems when it is waiting answers 24/6/2003 Josep Comas Remove thread to get answers and it uses a slow process 28/6/2003 Josep Comas Increased sleep 29/6/2003 Josep Comas Remove some commands and change others 1/9/2003 Josep Comas Added support for Zoom 5510 Added support for generic VID=0x0572, PID=0xCB01 and PID=0xCB06 6/9/2003 Josep Comas Added support for Draytek Vigor 318 */ /* Author : Josep Comas Creation : 24/1/2003 Description: This program controls the 'xdslusb' and 'cxacru' drivers 28/4/2003 Josep Comas Added support for Amigo AMX-CA86U and Olitec V3 30/4/2003 Josep Comas Added ioctls start and stop ADSL line 1/5/2003 Josep Comas Added support for Olitec V2 3/5/2003 Josep Comas Showing more information 5/5/2003 Josep Comas Update to work with libusb-0.1.7 3/6/2003 Josep Comas Added support for Amigo AMX-CA80U-2M Added more info 13/6/2003 Josep Comas Support for all modems in same executable 16/6/2003 Josep Comas Added units for attenuation, SNR and output power 1/9/2003 Josep Comas Added support for Zoom 5510 Added support for generic VID=0x0572, PID=0xCB01 and PID=0xCB06 6/9/2003 Josep Comas Added support for Draytek Vigor 318 2004-01-01 Peter G. Vast restructuring to improve usability and maintainability. Created a 'man' page too. 2004-01-03 Peter G. Even vaster restructuring, merging in 'cxload.c' and further cleanups. The bits merged in from 'cxload.c' haven't been much cleaned ip it. Updated the 'man' page. 2004-01-03b Peter G. Spent 1-2 hours rewriting the 'send_block' function which in the original form really upset me as to unnecessary complexity and opacity. The rewritten form pleases me, and might be an example of how to write, even if quickly, simpler, cleaner code. 2004-01-04 Peter G. More ``cosmetic'' cleanup. A lot of program logic could still be simplified and cleaned up, and the naming conventions are still rather inappropriate. */