diff -uNr slmodem-2.9.10/drivers/amrmo_init.c slmodem-2.9.10p/drivers/amrmo_init.c --- slmodem-2.9.10/drivers/amrmo_init.c 2004-06-23 16:29:06.000000000 -0300 +++ slmodem-2.9.10p/drivers/amrmo_init.c 2006-01-05 14:10:45.000000000 -0200 @@ -276,7 +276,7 @@ static struct amrmo_struct *amrmo_table[MAXNUM] = {}; #ifndef OLD_KERNEL -static struct class_simple *amrmo_class; +static struct class *amrmo_class; #endif /* @@ -620,7 +620,7 @@ } #endif #else - class_simple_device_add(amrmo_class, MKDEV(AMRMO_MAJOR, i), NULL, "slamr%d", i); + class_device_create(amrmo_class, MKDEV(AMRMO_MAJOR, i), NULL, "slamr%d", i); devfs_mk_cdev(MKDEV(AMRMO_MAJOR,i), S_IFCHR|S_IRUSR|S_IWUSR, "slamr%d", i); #endif return 0; @@ -652,7 +652,7 @@ } #endif #else - class_simple_device_remove(MKDEV(AMRMO_MAJOR, amrmo->num)); + class_device_destroy(amrmo_class, MKDEV(AMRMO_MAJOR, amrmo->num)); devfs_remove("slamr%d", amrmo->num); #endif amrmo_table[amrmo->num] = NULL; @@ -686,7 +686,7 @@ MODULE_AUTHOR("Smart Link Ltd."); MODULE_DESCRIPTION("SmartLink HAMR5600,SmartPCI56/561 based modem driver"); -MODULE_LICENSE("Smart Link Ltd."); +MODULE_LICENSE("GPL"); static int __init amrmo_init(void) @@ -708,7 +708,7 @@ #else while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { #endif - if(pci_match_device(amrmo_pci_tbl, dev) && + if(pci_match_id(amrmo_pci_driver.id_table, dev) && pci_dev_driver(dev)) { #ifdef OLD_KERNEL AMRMO_DBG("device %04x:%04x is used by %s: remove\n", @@ -732,7 +732,7 @@ } #ifndef OLD_KERNEL - amrmo_class = class_simple_create(THIS_MODULE, "slamr"); + amrmo_class = class_create(THIS_MODULE, "slamr"); if (IS_ERR(amrmo_class)) { int err = PTR_ERR(amrmo_class); printk(KERN_ERR "slamr: failure creating simple class, error %d\n", err); @@ -740,10 +740,10 @@ } #endif - if (!pci_register_driver(&amrmo_pci_driver)) { + if (pci_register_driver(&amrmo_pci_driver)) { pci_unregister_driver(&amrmo_pci_driver); #ifndef OLD_KERNEL - class_simple_destroy(amrmo_class); + class_destroy(amrmo_class); #endif return -ENODEV; } @@ -751,7 +751,7 @@ if(register_chrdev(AMRMO_MAJOR, "slamr", &amrmo_fops) < 0) { pci_unregister_driver(&amrmo_pci_driver); #ifndef OLD_KERNEL - class_simple_destroy(amrmo_class); + class_destroy(amrmo_class); #endif return -ENOMEM; } @@ -765,7 +765,7 @@ unregister_chrdev(AMRMO_MAJOR,"slamr"); pci_unregister_driver(&amrmo_pci_driver); #ifndef OLD_KERNEL - class_simple_destroy(amrmo_class); + class_destroy(amrmo_class); #endif } diff -uNr slmodem-2.9.10/drivers/st7554.c slmodem-2.9.10p/drivers/st7554.c --- slmodem-2.9.10/drivers/st7554.c 2004-06-23 17:20:34.000000000 -0300 +++ slmodem-2.9.10p/drivers/st7554.c 2006-01-05 14:14:50.000000000 -0200 @@ -206,7 +206,7 @@ static struct st7554_state *st7554_table[MAX_MODEMS] = {}; -static struct class_simple *st7554_class; +static struct class *st7554_class; static DECLARE_MUTEX(open_sem); @@ -1277,7 +1277,7 @@ } usb_set_intfdata(interface, s ); - class_simple_device_add(st7554_class, MKDEV(213, i), NULL, "slusb%d", i); + class_device_create(st7554_class, MKDEV(213, i), NULL, "slusb%d", i); devfs_mk_cdev(MKDEV(213,i),S_IFCHR|S_IRUSR|S_IWUSR,"slusb%d",i); USB_INFO(KERN_INFO "slusb: slusb%d is found.\n", s->minor); @@ -1306,7 +1306,7 @@ return; } - class_simple_device_remove(MKDEV(213, s->minor)); + class_device_destroy(st7554_class, MKDEV(213, s->minor)); devfs_remove("slusb%d",s->minor); st7554_stop(s); @@ -1340,7 +1340,7 @@ int ret; USB_INFO ("ST7554 USB Modem.\n"); - st7554_class = class_simple_create(THIS_MODULE, "slusb"); + st7554_class = class_create(THIS_MODULE, "slusb"); if (IS_ERR(st7554_class)) { ret = PTR_ERR(st7554_class); USB_ERR("st7554_modem_init: failed to create sysfs class, error %d\n", ret); @@ -1350,13 +1350,13 @@ ret = usb_register(&st7554_usb_driver); if ( ret ) { USB_ERR ("st7554_modem_init: cannot register usb device.\n"); - class_simple_destroy(st7554_class); + class_destroy(st7554_class); return ret; } if(register_chrdev(213, "slusb", &st7554_fops) < 0) { usb_deregister(&st7554_usb_driver); - class_simple_destroy(st7554_class); + class_destroy(st7554_class); return -ENOMEM; } return 0; @@ -1368,7 +1368,7 @@ USB_DBG ("st7554: exit...\n"); unregister_chrdev(213,"slusb"); usb_deregister(&st7554_usb_driver); - class_simple_destroy(st7554_class); + class_destroy(st7554_class); } diff -uNr slmodem-2.9.10/modem/modem.c slmodem-2.9.10p/modem/modem.c --- slmodem-2.9.10/modem/modem.c 2004-08-08 10:29:19.000000000 -0300 +++ slmodem-2.9.10p/modem/modem.c 2006-01-05 14:17:18.000000000 -0200 @@ -1695,10 +1695,10 @@ return 0; } +static int sregs_init(unsigned char sregs[]); int modem_reset(struct modem *m) { - static int sregs_init(unsigned char sregs[]); MODEM_DBG("modem reset...\n"); if(m->state != STATE_MODEM_IDLE) modem_hup(m,1); diff -uNr slmodem-2.9.10/modem/modem.c.orig slmodem-2.9.10p/modem/modem.c.orig --- slmodem-2.9.10/modem/modem.c.orig 1969-12-31 21:00:00.000000000 -0300 +++ slmodem-2.9.10p/modem/modem.c.orig 2004-08-08 10:29:19.000000000 -0300 @@ -0,0 +1,1969 @@ + +/* + * + * Copyright (c) 2002, Smart Link Ltd. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * 3. Neither the name of the Smart Link Ltd. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +/* + * + * modem.c -- modem core module. + * + * Author: Sasha K (sashak@smlink.com) + * + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + +#define XMIT_SIZE 4096 + +#define MODEM_AUTHOR "Smart Link Ltd." +#define MODEM_NAME "SmartLink Soft Modem" +#define MODEM_VERSION "2.9.10" +#define MODEM_DATE __DATE__" "__TIME__ + +/* event mask */ +#define MDMEVENT_RING_CHECK 0x01 +#define MDMEVENT_ESCAPE 0x02 + +/* debug prints */ +#define MODEM_INFO(fmt,arg...) printf(fmt , ##arg) ; dprintf(fmt , ##arg) +#define MODEM_DBG(fmt,arg...) dprintf("%s: " fmt , m->name , ##arg) +#define MODEM_ERR(fmt,arg...) eprintf("%s: " fmt , m->name , ##arg) + +/* external symbols */ +extern int process_at_command(struct modem *m, char *buf); +extern void *dp_runtime_create(struct modem *m); +extern void dp_runtime_delete(void *runtime); +extern void *dcr_create(); +extern void dcr_delete(void *dcr); +extern void dcr_process(void *dcr, void *buf, int len); +#ifdef MODEM_CONFIG_RING_DETECTOR +extern void *RD_create(struct modem *m, unsigned rate); +extern void RD_delete(void *obj); +extern int RD_process(void *obj, void *in, int count); +extern void RD_ring_details(void *obj, long *freq, long *duration); +#endif +#ifdef MODEM_CONFIG_CID +extern void *CID_create(struct modem *m, unsigned rate, unsigned cid_val); +extern void CID_delete(void *cid); +extern int CID_process(void *cid, void *in, int count); +#endif +#ifdef MODEM_CONFIG_VOICE +extern void *VOICE_create(struct modem *m, unsigned srate); +extern void VOICE_delete(void *obj); +extern int VOICE_process(void *obj, void *in, void *out, int count); +extern int VOICE_command(void *obj, enum VOICE_CMD cmd); +#endif +#ifdef MODEM_CONFIG_FAX +extern void *FAX_create(struct modem *m, unsigned caller, unsigned srate); +extern int FAX_process(void *obj, void *in, void *out, int count); +extern void FAX_delete(void *obj); +#endif + +/* local prototypes */ +int modem_answer(struct modem *m); +static void do_modem_change_dp(struct modem *); +static int modem_start(struct modem *); +static int modem_stop (struct modem *); +static struct dp_operations *get_dp_operations(enum DP_ID id); +static int modem_get_chars(struct modem *m, char *buf, int n); +static int modem_put_chars(struct modem *m, const char *buf, int n); +static int modem_comp_get_chars(struct modem *m, char *buf, int n); +static int modem_comp_put_chars(struct modem *m, const char *buf, int n); +#ifdef MODEM_CONFIG_CID +static int modem_cid_start(struct modem *, unsigned timeout); +#endif +#ifdef MODEM_CONFIG_FAX +static int modem_fax_start(struct modem *m); +#endif + +/* global config data */ +const char *modem_default_country = NULL; + + +/* data definitions */ +const char modem_author[] = MODEM_AUTHOR; +const char modem_name[] = MODEM_NAME; +const char modem_version[]= MODEM_VERSION; +const char modem_date[] = MODEM_DATE; + + +/* + * misc macros + */ + +/* ring parameter */ +#define RING_ON_MIN(m) MODEM_HZ*3/20 /* 0.15 sec */ +#define RING_ON_MAX(m) MODEM_HZ*5/2 /* 2.5 sec */ +#define RING_OFF_MIN(m) MODEM_HZ*3/2 /* 1.5 sec */ +#define RING_OFF_MAX(m) MODEM_HZ*13/2 /* 6.5 sec */ +#define RING_COUNT_MIN(m) 10 + +#define TOTAL_RINGS_COUNT(m) ((m)->sregs[SREG_RING_COUNTER]) +#define ANSWER_AFTER_RINGS(m) ((m)->sregs[SREG_RINGS_TO_AUTO_ANSWER]) + +#define ESCAPE_CHAR(m) ((m)->sregs[SREG_ESCAPE_CHAR]) +#define CR_CHAR(m) ((m)->sregs[SREG_CR_CHAR]) +#define LF_CHAR(m) ((m)->sregs[SREG_LF_CHAR]) +#define BS_CHAR(m) ((m)->sregs[SREG_BS_CHAR]) + +#define IS_ECHO(m) ((m)->sregs[SREG_ECHO]) +#define IS_QUIET(m) ((m)->sregs[SREG_QUIET]) +#define IS_VERBOSE(m) ((m)->sregs[SREG_VERBOSE]) +#define IS_AUTOMODE(m) MODEM_AUTOMODE(m) +#define MODEM_EC_ENABLE(m) ((m)->sregs[SREG_EC]) +#define MODEM_EC_DETECTOR(m) ((m)->sregs[SREG_EC]) +#define MODEM_COMP_ENABLE(m) ((m)->sregs[SREG_COMP]&&MODEM_EC_ENABLE(m)) +#define ESCAPE_TIMEOUT(m) (MODEM_HZ/2) +#define ANSWER_DELAY(m) ((m)->sregs[SREG_ANS_DELAY]) +#define SPEAKER_CONTROL(m) ((m)->sregs[SREG_SPEAKER_CONTROL]) +#define SPEAKER_VOLUME(m) ((m)->sregs[SREG_SPEAKER_VOLUME]) + +#define QC_SKIP_EC_DETECTION(m) ((m)->caller && (m)->dsp_info.qc_lapm) + + +/* + * dp operations drivers + * + */ + +struct dp_driver modem_dp_drivers[] = { + {DP_CALLPROG,"CallProg"}, + {DP_DUMMY,"Dummy"}, + {DP_AUTOMODE,"Automode"}, + {DP_V8,"V8"}, + {DP_V17,"V17"}, + {DP_V21,"V21","21"}, + {DP_V22,"V22","22"}, + {DP_V23,"V23","23"}, + {DP_V22BIS,"V22bis","122"}, + {DP_V32,"V32","32"}, + {DP_V32BIS,"V32bis","132"}, + {DP_V34,"V34","34"}, + {DP_B103,"Bell103","103"}, + {DP_B212,"Bell212","212"}, + {DP_FAX,"VFax"}, + {DP_K56,"K56Flex","56"}, + {DP_V8BIS,"V8bis"}, + {DP_V90,"V90","90"}, + {DP_V92,"V92","92"}, + {DP_SINUS,"Sinus"}, + {} +}; + +static struct dp_driver *get_dp_driver(enum DP_ID id) +{ + struct dp_driver *p; + for(p = modem_dp_drivers ; p->id > 0 ; p++) + if (p->id == id) return p; + return NULL; +} + +static struct dp_operations *get_dp_operations(enum DP_ID id) +{ + struct dp_driver *p; + for(p = modem_dp_drivers ; p->id > 0 ; p++) + if (p->id == id) return p->op; + return NULL; +} + +int modem_dp_register(enum DP_ID id, struct dp_operations *op) +{ + struct dp_driver *p; + int ret = -1; + for(p = modem_dp_drivers ; p->id > 0 ; p++) { + if (p->id == id && !p->op) { + p->op = op; + ret = 0; + break; + } + } + return ret; +} + +void modem_dp_deregister(enum DP_ID id, struct dp_operations *op) +{ + struct dp_driver *p; + for(p = modem_dp_drivers ; p->id > 0 ; p++) + if (p->id == id) p->op = NULL; +} + + + +/* + * state and status handling + * + */ + +enum MODEM_RESULT { + RESULT_OK = 0, + RESULT_CONNECT = 1, + RESULT_RING = 2, + RESULT_NOCARRIER = 3, + RESULT_ERROR = 4, + RESULT_UNUSED_5 = 5, + RESULT_NODIALTONE = 6, + RESULT_BUSY = 7, + RESULT_NOANSWER = 8, +}; + +static void modem_report_result(struct modem *m, enum MODEM_RESULT code) +{ +#define MODEM_RESPONSE_TEXT(id) modem_responses[id].text +#define MODEM_RESPONSE_CODE(id) modem_responses[id].code + /* response codes : FIXME */ + static const struct modem_response { + enum MODEM_RESULT id; + char *code; + char *text; + } modem_responses[] = { + {RESULT_OK, "0", "OK"}, + {RESULT_CONNECT, "1", "CONNECT"}, + {RESULT_RING, "2", "RING"}, + {RESULT_NOCARRIER, "3", "NO CARRIER"}, + {RESULT_ERROR, "4", "ERROR"}, + {RESULT_UNUSED_5, "" , ""}, + {RESULT_NODIALTONE, "6", "NO DIALTONE"}, + {RESULT_BUSY, "7", "BUSY"}, + {RESULT_NOANSWER, "8", "NO ANSWER"}, + }; + static const char none_str[] = "NONE"; + const char *msg; + u8 msg_mask; + + MODEM_DBG("modem report result: %d (%s)\n",code,MODEM_RESPONSE_TEXT(code)); + if (IS_QUIET(m)) + return; + if (IS_VERBOSE(m)) { + if(code==RESULT_CONNECT) { + msg_mask = modem_get_sreg(m,SREG_CONNNECT_MSG_FORMAT); + if(msg_mask&1) { + struct dp_driver *dp_drv; + modem_put_chars(m,"Modulation: ",12); + dp_drv = get_dp_driver(m->dp->id); + msg = (dp_drv) ? dp_drv->name : none_str; + modem_put_chars(m,msg,strlen(msg)); + modem_put_chars(m,CRLF_CHARS(m),2); + } + if(msg_mask&2) { + modem_put_chars(m,"Protocol: ",10); + msg = m->cfg.ec ? "LAPM" : none_str; + modem_put_chars(m,msg,strlen(msg)); + modem_put_chars(m,CRLF_CHARS(m),2); + } + if((msg_mask&3) == 3) { + modem_put_chars(m,"Compression: ",13); + msg = (m->cfg.ec && m->cfg.comp)? "V42bis" : none_str; + modem_put_chars(m,msg,strlen(msg)); + modem_put_chars(m,CRLF_CHARS(m),2); + } + if(msg_mask&4 && m->tx_rate) { + char rate_str[16]; + modem_put_chars(m,"TxRate: ",8); + sprintf(rate_str,"%d",m->tx_rate); + modem_put_chars(m,rate_str,strlen(rate_str)); + modem_put_chars(m,CRLF_CHARS(m),2); + } + } + msg = MODEM_RESPONSE_TEXT(code); + modem_put_chars(m,msg,strlen(msg)); + if (code==RESULT_CONNECT && m->mode == MODEM_MODE_DATA + && m->rx_rate && modem_get_sreg(m,SREG_X_CODE) != 0) { + char rate_str[16]; + sprintf(rate_str," %d",m->rx_rate); + modem_put_chars(m,rate_str,strlen(rate_str)); + } + } + else { + msg = MODEM_RESPONSE_CODE(code); + modem_put_chars(m,msg,strlen(msg)); + } + modem_put_chars(m,CRLF_CHARS(m),2); +} + + +/* TDB: state lifecycle description */ + +/* modem state constants */ +#define STATE_MODEM_IDLE 0x1 +#define STATE_DP_ESTAB 0x2 +#define STATE_EC_ESTAB 0x4 +#define STATE_MODEM_ONLINE 0x5 +#define STATE_COMMAND_ONLINE 0x6 +#define STATE_EC_DISC 0x7 +#define STATE_DP_DISC 0x9 + +#define STATE_DISC 0x100 + +#define STATE_ESTAB STATE_DP_ESTAB + +#define IS_STATE_LINKED(stat) ((stat) > STATE_DP_ESTAB) + +#define IS_STATE_IDLE(stat) ((stat) == STATE_MODEM_IDLE) +#define IS_STATE_CONNECTING(stat) ((stat) > STATE_MODEM_IDLE && (stat) < STATE_MODEM_ONLINE) +#define IS_STATE_ONLINE(stat) ((stat) == STATE_MODEM_ONLINE || (stat) == STATE_COMMAND_ONLINE) +#define IS_STATE_DISC(stat) ((stat) == STATE_EC_DISC || (stat) == STATE_DP_DISC) + + +static int modem_set_state(struct modem *m, unsigned new_state) +{ + MODEM_DBG("modem set state: %x --> %x...\n", + m->state, new_state); + if(m->state == new_state) + return 0; + switch(new_state) { + case STATE_MODEM_IDLE: + MODEM_DBG("new state: MODEM_IDLE\n"); + m->command = 1; + m->get_chars = 0; + m->put_chars = 0; + m->get_bits = 0; + m->put_bits = 0; + break; + case STATE_DP_ESTAB: + MODEM_DBG("new state: DP_ESTAB\n"); + m->command = 0; + m->get_chars = 0; + m->put_chars = 0; + m->get_bits = 0; + m->put_bits = 0; + break; + case STATE_EC_ESTAB: + MODEM_DBG("new state: EC_ESTAB\n"); + m->command = 0; + m->get_chars = 0; + m->put_chars = 0; + break; + case STATE_MODEM_ONLINE: + MODEM_DBG("new state: MODEM_ONLINE\n"); + m->command = 0; + m->put_chars = (m->cfg.ec && m->cfg.comp&0x1) + ? modem_comp_put_chars : modem_put_chars; + m->get_chars = (m->cfg.ec && m->cfg.comp&0x2) + ? modem_comp_get_chars : modem_get_chars; + m->modem_info |= TIOCM_CD; + break; + case STATE_COMMAND_ONLINE: + MODEM_DBG("new state: COMMAND_ONLINE\n"); + m->command = 1; + m->get_chars = 0; + m->put_chars = 0; + break; + case STATE_EC_DISC: + MODEM_DBG("new state: EC_DISC\n"); + m->get_chars = 0; + m->put_chars = 0; + break; + case STATE_DP_DISC: + MODEM_DBG("new state: DP_DISC\n"); + m->get_chars = 0; + m->put_chars = 0; + m->get_bits = 0; + m->put_bits = 0; + break; + default: + MODEM_ERR("invalid state: %d\n", new_state); + return -1; + } + m->state = new_state; + return 0; +} + + +static void run_modem_stop(struct modem *m) +{ + modem_stop(m); +} + +/* FIXME */ +#define schedule_modem_stop(m) { m->sample_timer_func = run_modem_stop; \ + m->sample_timer = m->count + 48; } + + +static void modem_hup(struct modem *m, unsigned local) +{ + MODEM_DBG("modem_hup...\n"); + switch(m->state) { + case STATE_MODEM_IDLE: + return; + case STATE_DP_ESTAB: + if(local && m->dp && m->dp->op->hangup ) + m->dp->op->hangup(m->dp); + modem_set_state(m,STATE_DP_DISC); + if (m->result_code == 0) + m->result_code = RESULT_NOCARRIER; + schedule_modem_stop(m); + break; + case STATE_EC_ESTAB: + case STATE_MODEM_ONLINE: + case STATE_COMMAND_ONLINE: + if(local && m->cfg.ec) { + modem_ec_stop(m); + modem_set_state(m,STATE_EC_DISC); + } + else { + if(local && m->dp && m->dp->op->hangup) + m->dp->op->hangup(m->dp); + modem_set_state(m,STATE_DP_DISC); + } + if (m->result_code == 0) + m->result_code = RESULT_NOCARRIER; + schedule_modem_stop(m); +#if ALREADY_DONE + if(m->modem_info&TIOCM_CD && m->tty && + !m->termios->c_cflag&CLOCAL /* !C_CLOCAL(m->tty) */) { + MODEM_DBG("tty_hangup...\n"); + tty_hangup(m->tty); + } +#endif + m->modem_info &= ~TIOCM_CD; + break; + case STATE_EC_DISC: + if(local && m->dp && m->dp->op->hangup ) + m->dp->op->hangup(m->dp); + modem_set_state(m,STATE_DP_DISC); + break; + case STATE_DP_DISC: + default: + return; + } +} + + +/* FIXME: find better place */ +#define NONEC_DP(id) ((id) == DP_V23 || (id) == DP_B103 || (id) == DP_V21) + +void modem_update_status(struct modem *m, unsigned status) +{ + MODEM_DBG("modem_update_status: %d\n", status); + switch(status) { + case STATUS_OK: + break; + case STATUS_DP_LINK: + MODEM_DBG("--> DP LINK\n"); + if(NONEC_DP(m->dp->id)) + m->cfg.ec = 0; + modem_set_state(m,STATE_EC_ESTAB); + /* start packer */ + if(m->cfg.ec && m->cfg.ec_detector && + !QC_SKIP_EC_DETECTION(m) ) { + modem_detector_start(m); + m->get_bits = modem_detector_get_bits; + m->put_bits = modem_detector_put_bits; + } + else { /* skip pack connect phase */ + modem_update_status(m,STATUS_PACK_LINK); + } + break; + case STATUS_PACK_LINK: + MODEM_DBG("--> PACK LINK\n"); + if(m->cfg.ec) { + /* start EC */ + modem_hdlc_start(m); + m->get_bits = modem_hdlc_get_bits; + m->put_bits = modem_hdlc_put_bits; + modem_ec_start(m); + } + else { + /* start async, pass EC connect phase */ + modem_async_start(m); + m->get_bits = modem_async_get_bits; + m->put_bits = modem_async_put_bits; + modem_update_status(m,STATUS_EC_LINK); + } + break; + /* case STATUS_CONNECT: */ + case STATUS_EC_LINK: + MODEM_DBG("--> EC LINK\n"); + modem_set_state(m,STATE_MODEM_ONLINE); + modem_report_result(m,RESULT_CONNECT); // fixme + if(SPEAKER_CONTROL(m) == 1) + m->driver.ioctl(m,MDMCTL_SPEAKERVOL,0); + break; + case STATUS_EC_RELEASE: + case STATUS_EC_ERROR: + MODEM_DBG("--> EC UNLINK\n"); + m->result_code = RESULT_NOCARRIER; + modem_hup(m,(m->state == STATE_EC_DISC)); + break; + case STATUS_ERROR: + case STATUS_DP_ERROR: + case STATUS_NOCARRIER: + case STATUS_NODIALTONE: + case STATUS_BUSY: + case STATUS_NOANSWER: + default: + MODEM_DBG("--> FINISH.\n"); + if(status == STATUS_NODIALTONE) + m->result_code = RESULT_NODIALTONE; + else if(status == STATUS_BUSY) + m->result_code = RESULT_BUSY; + else if(status == STATUS_NOANSWER) + m->result_code = RESULT_NOANSWER; + else + m->result_code = RESULT_NOCARRIER; + modem_hup(m,IS_STATE_DISC(m->state)); + break; + } +} + + + +void modem_update_config(struct modem *m, struct modem_config *cfg) +{ + MODEM_DBG("modem update config...\n"); +#if 1 +#define PRINT_CONFIG(prm) MODEM_DBG(#prm " = %d (%d)\n",cfg->prm,m->cfg.prm) + PRINT_CONFIG(ec); + PRINT_CONFIG(ec_detector); + PRINT_CONFIG(ec_tx_win_size); + PRINT_CONFIG(ec_rx_win_size); + PRINT_CONFIG(ec_tx_info_size); + PRINT_CONFIG(ec_rx_info_size); + PRINT_CONFIG(comp); + PRINT_CONFIG(comp_dict_size); + PRINT_CONFIG(comp_max_string); +#endif + // FIXME, FIXME, FIXME !!! +#if 0 + if(cfg->ec) ; /* TBD: now ec reconfig is handled internally in EC */ +#endif + if(cfg->comp && m->cfg.comp) { + m->cfg.comp = cfg->comp; + if(!modem_comp_config(m,cfg->comp_dict_size, + cfg->comp_max_string)) { + m->cfg.comp_dict_size = cfg->comp_dict_size; + m->cfg.comp_max_string = cfg->comp_max_string; + } + else { /* config failed */ + ; /* TBD: probably try to renegotiate */ + } + } + else + m->cfg.comp = 0; +} + + +/* + * Interrupt handlers and services + * + */ + +/* get/put bits */ +int modem_get_bits(struct modem *m, int nbits, u8 *buf, int n) +{ + if(m->get_bits) + n = m->get_bits(m,nbits,buf,n); + else + memset(buf,0xff,n); + modem_debug_log_data(m,MODEM_DBG_TX_BITS,buf,n); + /* process bit timer and packer */ + if (m->bit_timer && (m->bit_timer -= nbits*n) <= 0) { + m->bit_timer = 0; + m->bit_timer_func(m); + } + // FIXME NOW: remove packer_process from modem to EC + if(m->packer_process) + m->packer_process(m,nbits*n); + return n; +} + +int modem_put_bits(struct modem *m, int nbits, u8 *buf, int n) +{ + if(m->put_bits) + n = m->put_bits(m,nbits,buf,n); + modem_debug_log_data(m,MODEM_DBG_RX_BITS,buf,n); + return n; +} + + +static unsigned dpstat2status(unsigned dpstat) +{ + switch (dpstat) { + case DPSTAT_OK: return STATUS_OK; + case DPSTAT_CONNECT: return STATUS_DP_LINK; + case DPSTAT_ERROR: return STATUS_DP_ERROR; + case DPSTAT_NODIALTONE: return STATUS_NODIALTONE; + case DPSTAT_BUSY: return STATUS_BUSY; + case DPSTAT_NOANSWER: return STATUS_NOANSWER; + case DPSTAT_CHANGEDP: return STATUS_OK; + default: return STATUS_ERROR; + } +} + + +static void modem_dp_process(struct modem *m,void *in,void *out,int count) +{ + int ret; + unsigned cnt; + //MODEM_DBG("modem_process %d: %d...\n", m->count,count); + while(count && m->dp) { + cnt = count; + if (cnt > m->frag) + cnt = m->frag; + //MODEM_DBG("%d: running dp %s...\n", m->count,dp->name); + ret = m->dp->op->process(m->dp,in,out,cnt); + switch(ret) { + case DPSTAT_OK: + break; + case DPSTAT_CONNECT: + if(!IS_STATE_LINKED(m->state) && + !IS_STATE_DISC(m->state)) { + modem_update_status(m,STATUS_DP_LINK); + } + break; + case DPSTAT_CHANGEDP: + do_modem_change_dp(m); + break; + default: /* errors */ + modem_update_status(m,dpstat2status(ret)); + m->process = NULL; + break; + } + + count -= cnt; + out += cnt<format); + in += cnt<format); + } + if(count) + memset(out,0,count<format)); +} + + +void modem_process(struct modem *m,void *in,void *out,int count) +{ + /* clean DC */ + dcr_process(m->dcr,in,count); + modem_debug_log_data(m,MODEM_DBG_RX_SAMPLES,in,count<format)); + if(m->process) + m->process(m,in,out,count); + else /* mute output */ + memset(out,0,count<format)); + modem_debug_log_data(m,MODEM_DBG_TX_SAMPLES,out,count<format)); + m->count+=count; + if( m->sample_timer && m->count >= m->sample_timer ) { + void (*f)(struct modem *) = m->sample_timer_func ; + m->sample_timer = 0; + m->sample_timer_func = NULL; + if(f) f(m); + } +} + + +/* + * event processing + * + */ + + +static void timer_mark_event (void *data) +{ + struct modem *m = data; +#ifdef DEBUG_TIMERS + MODEM_DBG("timer_mark_event: %x | %x : now = %lu...\n", + m->event, m->new_event, get_time()); +#endif + m->event |= m->new_event; + m->new_event = 0; +} + +static void schedule_event(struct modem *m, unsigned mask, unsigned long when) +{ + m->new_event |= mask; + m->event_timer.data = m; + m->event_timer.func = timer_mark_event; + m->event_timer.expires = get_time() + when; +#ifdef DEBUG_TIMERS + MODEM_DBG("schedule_event: %x | %x : now %lu + %lu = %u...\n", + m->new_event, mask, + get_time(), when, m->event_timer.expires); +#endif + timer_add(&m->event_timer); +} + + +void modem_event(struct modem *m) +{ + unsigned event = m->event; + m->event = 0; + MODEM_DBG("modem_event: %x...\n", event); + + if(event&MDMEVENT_RING_CHECK) { + if(m->ring_count == 0) { + MODEM_DBG("ring cancel...\n"); + TOTAL_RINGS_COUNT(m) = 0; + } + else if ( time_before(m->ring_last,m->ring_first+RING_ON_MIN(m)) || + time_after(m->ring_last,m->ring_first+RING_ON_MAX(m)) || + m->ring_count < RING_COUNT_MIN(m)) { + MODEM_DBG("ring reject: count %d (%lu-%lu)\n", + m->ring_count,m->ring_first,m->ring_last); + TOTAL_RINGS_COUNT(m) = 0; + m->ring_count = 0; + } + else { + MODEM_DBG("ring valid.\n"); + m->ring_count = 0; + TOTAL_RINGS_COUNT(m)++; + if(TOTAL_RINGS_COUNT(m) == 1) + modem_put_chars(m,CRLF_CHARS(m),2); + modem_report_result(m,RESULT_RING); + if (ANSWER_AFTER_RINGS(m) && +#ifdef MODEM_CONFIG_RING_DETECTOR + (!m->started || m->rd_obj) && +#else + !m->started && +#endif + TOTAL_RINGS_COUNT(m) >= ANSWER_AFTER_RINGS(m)) { + TOTAL_RINGS_COUNT(m) = 0; + modem_answer(m); + } + else { + schedule_event(m,MDMEVENT_RING_CHECK, + RING_OFF_MAX(m) + 1); + } + } + } + if(event&MDMEVENT_ESCAPE) { + MODEM_DBG("modem_escape (count %d)...\n", m->escape_count); + if (m->state == STATE_MODEM_ONLINE && m->escape_count == 3) { + modem_set_state(m, STATE_COMMAND_ONLINE); + m->xmit.head = m->xmit.tail = m->xmit.count = 0; + modem_put_chars(m,CRLF_CHARS(m),2); + modem_report_result(m,RESULT_OK); + m->command = 1; + } + m->escape_count = 0; + } +} + +void modem_ring(struct modem *m) +{ + unsigned long now = get_time(); + if (m->state != STATE_MODEM_IDLE) + return; + m->ring_count++; + if(m->ring_count == 1) { + MODEM_DBG("modem_ring...\n"); + if(time_before(now,m->ring_last + RING_OFF_MIN(m))) { + MODEM_DBG("bad ring: now %lu, last %lu.\n", + now,m->ring_last); + m->ring_count = 0; + } + else { + m->ring_first = now; + schedule_event(m,MDMEVENT_RING_CHECK, + RING_ON_MAX(m) + 1); +#ifdef MODEM_CONFIG_CID + if (TOTAL_RINGS_COUNT(m) == 0 && m->cid_requested) + modem_cid_start(m, RING_ON_MAX(m) + RING_OFF_MIN(m)); +#endif + } + } + m->ring_last = now; +} + + +void modem_error(struct modem *m) +{ + MODEM_DBG("modem error...\n"); +} + + + +/* command mode processing */ +static void modem_at_process(void *data) +{ + struct modem *m = (struct modem *)data; + int echo = IS_ECHO(m); + char lf = LF_CHAR(m), cr = CR_CHAR(m), bs = BS_CHAR(m); + int ret; + char ch; + while(1) { + if(modem_get_chars(m,&ch,1) <= 0) + break; + if (ch == '/' && m->at_count == 1 && + toupper(m->at_line[0]) == 'A') { + m->at_count = strlen(m->at_line); + if(echo) + modem_put_chars(m,m->at_line+1,m->at_count-1); + ch = cr; + } + if (echo) + modem_put_chars(m,&ch,1); + if (ch == cr || ch == lf) { + int i; + char *p = m->at_cmd; + m->at_line[m->at_count] = '\0'; + for(i = 0 ; i < m->at_count ; i++) { + if( m->at_line[i] == ' ' || + m->at_line[i] == '\t' ) + continue; + *p++ = m->at_line[i]; + } + *p = '\0'; + if (strlen(m->at_cmd) < 2 || + toupper(m->at_cmd[0]) != 'A' || + toupper(m->at_cmd[1]) != 'T' ) { + memset(m->at_line,' ',m->at_count); + if(echo) { + modem_put_chars(m,&cr,1); + modem_put_chars(m,m->at_line,m->at_count); + modem_put_chars(m,&cr,1); + } + m->at_count = 0; + continue; + } + if (echo) + modem_put_chars(m,CRLF_CHARS(m),2); + MODEM_DBG("run cmd: %s\n",m->at_cmd); + ret = process_at_command(m, m->at_cmd); + if (ret < 0) + modem_report_result(m,RESULT_ERROR); + else if (ret==0) + modem_report_result(m,RESULT_OK); + m->at_line[m->at_count] = '\0'; + m->at_count = 0; + echo = IS_ECHO(m); + cr = CR_CHAR(m), lf = LF_CHAR(m), bs = BS_CHAR(m); + } + else if (ch == bs && m->at_count) { + m->at_count--; + if(echo) { + ch = ' '; + modem_put_chars(m,&ch,1); + modem_put_chars(m,&bs,1); + } + } + else if (m->at_count == sizeof(m->at_line) - 1) { + if (echo) + modem_put_chars(m,CRLF_CHARS(m),2); + m->at_count = 0; + m->at_line[1] = '\0'; + modem_report_result(m,RESULT_ERROR); + } + else { + m->at_line[m->at_count++] = ch; + } + } +} + + +/* + * TTY procedures + * + */ + + +/* get chars */ +static int modem_get_chars(struct modem *m, char *buf, int n) +{ + int ret = 0, cnt; + while(n) { + cnt = n; + if (cnt > m->xmit.count) + cnt = m->xmit.count; + if (cnt > m->xmit.size - m->xmit.tail) + cnt = m->xmit.size - m->xmit.tail; + if (cnt <= 0) { + break; + } + memcpy(buf, m->xmit.buf + m->xmit.tail, cnt); + ret += cnt; + n -= cnt; + buf += cnt; + m->xmit.count -= cnt; + m->xmit.tail = (m->xmit.tail+cnt)%m->xmit.size; + } + //MODEM_DBG("modem_get_chars: %d...\n", n); + return ret; +} + +/* put chars */ +static int modem_put_chars(struct modem *m, const char *buf, int n) +{ + int ret = write(m->pty,buf,n); + if(ret < 0) { + /* perror("write"); */ + ret = 0; + } +#if 0 + if(ret>0) { + //MODEM_DBG("modem_comp_put_chars: %d...\n",i); + modem_debug_log_data(m, MODEM_DBG_RX_CHARS,buf,i); + } +#endif + return ret; +} + +/* compressor get/put chars */ +/* fixme: unify interfaces: modem tx -> comp -> ec -> hdlc --> + modem tx -> ec -> hdlc --> + modem tx -> async --> + modem tx --> raw output +*/ +static int modem_comp_get_chars(struct modem *m, char *buf, int n) +{ + int ret = 0, cnt; + char ch; + while(ret < n) { + cnt = modem_get_chars(m,&ch,1); + if(cnt <= 0) + break; + cnt = modem_comp_encode(m,ch,buf+ret,n-ret); + if(cnt < 0) { + break; + } + ret += cnt; + } + if(ret < n) { + cnt = modem_comp_flush_encoder(m,buf+ret,n-ret); + ret += cnt; + } + if(ret > 0) { + //MODEM_DBG("modem_comp_get_chars: %d(%d)...\n",ret,count); + modem_debug_log_data(m,MODEM_DBG_TX_DATA,buf,ret); + } + return ret; +} + +static int comp_send_output(struct modem *m) +{ + int cnt; + if(m->comp.count > 0) { + cnt = modem_put_chars(m,m->comp.buf+m->comp.head,m->comp.count); + m->comp.count -= cnt; + m->comp.head += cnt; + if(m->comp.count > 0) + return 0; + else + m->comp.head = 0; + } + return 1; +} + +static int modem_comp_put_chars(struct modem *m, const char *buf, int n) +{ + int i=0, cnt; + if(!comp_send_output(m)) + return 0; + while(icomp.buf,sizeof(m->comp.buf)); + if(cnt < 0) { + MODEM_DBG("decoder error. %d(%d)\n",i,n); + modem_update_status(m,STATUS_ERROR); + break; + } + m->comp.count += cnt; + if(!comp_send_output(m)) + break; + } + if(i==n) { + cnt = modem_comp_flush_decoder(m,m->comp.buf,sizeof(m->comp.buf)); + m->comp.count += cnt; + comp_send_output(m); + } + if(i>0) { + //MODEM_DBG("modem_comp_put_chars: %d...\n",i); + modem_debug_log_data(m,MODEM_DBG_RX_DATA,buf,i); + } + return i; +} + + + + +/* + * internal procedures + * + */ + +#define IS_FAST_DP(id) (id == DP_V34 || id == DP_V90 || id == DP_V92) + +static void do_modem_change_dp (struct modem *m) +{ + struct dp_operations *op; + struct dp *old; + + old = m->dp; + + if (m->mode == MODEM_MODE_FAX) { + modem_fax_start(m); + m->dp = NULL; + } + else { + struct dp *dp = NULL; + int dp_id,id; + dp_id = m->dp_requested; + m->dp_requested = 0; + + id = dp_id; + if(dp_id == 0) { + dp_id = MODEM_DP(m); + id = IS_FAST_DP(dp_id) ? DP_V8 : dp_id; + } + + MODEM_DBG("%ld: change dp: --> %d...\n", m->count, id); + + op = get_dp_operations(id); + if (op && op->create) + dp = op->create(m,dp_id, + m->caller,m->srate, + m->frag,op); + if (!dp) { + MODEM_ERR("change dp -> %d error.\n", id); + modem_hup(m,1); + return; + } + + m->dp = dp; + m->process = modem_dp_process; + } + + if ( old ) { + old->op->delete(old); + } + +} + + +static int modem_set_hook(struct modem *m, unsigned hook_state) +{ + int ret; + MODEM_DBG("modem set hook: %d --> %d...\n", m->hook, hook_state); + if ( m->hook == hook_state ) + return 0; + ret = m->driver.ioctl(m, MDMCTL_HOOKSTATE,hook_state); + if (!ret) + m->hook = hook_state; + return ret; +} + +static void modem_setup_config(struct modem *m) +{ + m->cfg.ec = MODEM_EC_ENABLE(m); + m->cfg.ec_detector = MODEM_EC_DETECTOR(m); + m->cfg.ec_tx_win_size = LAPM_MAX_WIN_SIZE; + m->cfg.ec_rx_win_size = LAPM_MAX_WIN_SIZE; + m->cfg.ec_tx_info_size = LAPM_MAX_INFO_SIZE; + m->cfg.ec_rx_info_size = LAPM_MAX_INFO_SIZE; + if(m->cfg.ec && MODEM_COMP_ENABLE(m)) + m->cfg.comp = 0x3; /* bi-directional v42bis */ + else + m->cfg.comp = 0; + m->cfg.comp_dict_size = COMP_MAX_CODEWORDS; + m->cfg.comp_max_string = COMP_MAX_STRING; + + /* setup dsp data */ + m->dsp_info.qc_lapm = m->cfg.ec && m->cfg.ec_detector ; +} + + +static int do_modem_start(struct modem *m) +{ + int ret; + ret = m->driver.ioctl(m, MDMCTL_SPEED, m->srate); + ret = m->driver.ioctl(m, MDMCTL_SETFRAG, m->frag); + m->count = 0; + ret = m->driver.start(m); + m->started = !ret; + return ret; +} + +static int modem_start (struct modem *m) +{ + int ret; + + MODEM_DBG("modem_start..\n"); + if(m->started && modem_stop(m)) + return -1; + + m->result_code = 0; + modem_setup_config(m); + modem_set_state(m, STATE_ESTAB); + + if(SPEAKER_CONTROL(m) == 1) + m->driver.ioctl(m,MDMCTL_SPEAKERVOL,SPEAKER_VOLUME(m)); + ret = modem_set_hook(m, MODEM_HOOK_OFF); + if (ret) + goto error; + + m->xmit.head = m->xmit.tail = m->xmit.count = 0; + m->command = 0; + + if( !(m->dcr= dcr_create()) || + !(m->dp_runtime = dp_runtime_create(m))) { + ret = -1; + goto error; + } + + if ( m->cfg.ec && m->cfg.comp && + (ret = modem_comp_init(m)) ) + goto error; + if ( m->cfg.ec && + (ret = modem_ec_init(m))) + goto error; + + /* clear rings and all events */ + TOTAL_RINGS_COUNT(m) = 0; + m->ring_count = 0; + m->event = m->new_event = 0; + timer_del(&m->event_timer); + + ret = do_modem_start(m); + if (!ret) + return 0; + + error: + MODEM_ERR("modem start = %d: cannot start device.\n",ret); + m->result_code = RESULT_NOCARRIER; + modem_stop(m); + return ret; +} + + +static int modem_stop (struct modem *m) +{ + int ret = 0; + MODEM_DBG("modem_stop..\n"); + + m->process = NULL; + + if(m->started) { + ret = m->driver.stop(m); + m->started = ret; + if ( ret ) { + MODEM_ERR("modem stop = %d: cannot stop device.\n",ret); + } + } + modem_set_hook(m, MODEM_HOOK_ON); + if(SPEAKER_CONTROL(m) == 1) + m->driver.ioctl(m,MDMCTL_SPEAKERVOL,0); + m->caller = 0; + m->command = 1; + + // FIXME: If ec,comp were allocated handled inside _exit() - improve? + modem_ec_exit(m); + modem_comp_exit(m); + + if (m->dp) { + struct dp *dp; + dp = m->dp; + m->dp = 0; + if(dp) dp->op->delete(dp); + } + if (m->dp_runtime) { + dp_runtime_delete(m->dp_runtime); + m->dp_runtime = NULL; + } + if (m->dcr) { + dcr_delete(m->dcr); + m->dcr = NULL; + } +#ifdef MODEM_CONFIG_RING_DETECTOR + if(m->rd_obj) { + RD_delete(m->rd_obj); + m->rd_obj = NULL; + } +#endif +#ifdef MODEM_CONFIG_CID + if(m->cid) { + CID_delete(m->cid); + m->cid = NULL; + } +#endif +#ifdef MODEM_CONFIG_VOICE + if(m->voice_obj) { + VOICE_delete(m->voice_obj); + m->voice_obj = NULL; + } +#endif +#ifdef MODEM_CONFIG_FAX + if(m->fax_obj) { + FAX_delete(m->fax_obj); + m->fax_obj = NULL; + } +#endif + modem_set_state(m, STATE_MODEM_IDLE); + if (m->result_code) { + modem_report_result(m, m->result_code); + m->result_code = 0; + } + + m->count = 0; + return m->started; +} + + +/* + * Caller ID + * + */ + +#ifdef MODEM_CONFIG_CID + +static void modem_cid_stop(struct modem *m) +{ + MODEM_DBG("modem_cid_stop...\n"); + m->sample_timer = 0; + m->sample_timer_func = NULL; +#ifdef MODEM_CONFIG_RING_DETECTOR + if(!m->rd_obj) { + m->process = NULL; + modem_stop(m); + } + else { + CID_delete(m->cid); + m->cid = NULL; + } +#else + m->process = NULL; + modem_stop(m); +#endif + /* continue with answer if need */ + if (ANSWER_AFTER_RINGS(m) && + TOTAL_RINGS_COUNT(m) >= ANSWER_AFTER_RINGS(m)) { + TOTAL_RINGS_COUNT(m) = 0; + modem_answer(m); + } +} + +static void modem_cid_process(struct modem *m, void *in, void *out, int count) +{ + int status; + //MODEM_DBG("modem_cid_process: %d...\n", count); + memset(out,0,count*2); + status = CID_process(m->cid, in, count); + if(status) { + if(status < 0) + MODEM_DBG("CID failed.\n"); + modem_cid_stop(m); + } +} + +static int modem_cid_start(struct modem *m, unsigned timeout) +{ + MODEM_DBG("modem_cid_start: timeout = %d...\n", timeout); +#ifdef MODEM_CONFIG_RING_DETECTOR + if(m->started && !m->rd_obj) +#else + if(m->started) +#endif + return -1; + m->cid = CID_create(m, m->srate, m->cid_requested); + if(!m->cid) + return -1; + m->sample_timer = m->count + m->srate*timeout/MODEM_HZ ; + m->sample_timer_func = modem_cid_stop; +#ifdef MODEM_CONFIG_RING_DETECTOR + if(m->started && m->rd_obj) + return 0; +#endif + m->process = modem_cid_process; + modem_set_hook(m, MODEM_HOOK_SNOOPING); + return do_modem_start(m); +} + +#endif /* MODEM_CONFIG_CID */ + + +/* + * Ring detector (internal) + * + */ + +#ifdef MODEM_CONFIG_RING_DETECTOR +static void modem_ring_detector_process(struct modem *m, void *in, void *out, int count) +{ + int ret; + memset(out, 0, count*2); + ret = RD_process(m->rd_obj, in, count); + if (ret) { + long freq, duration; + RD_ring_details(m->rd_obj, &freq, &duration); + MODEM_DBG("ring details: freq = %ld, duration = %ld\n", + freq, duration); + if(freq == 0) { + MODEM_DBG("report ring start...\n"); + modem_ring(m); + } + else if (freq > 0) { + MODEM_DBG("report ring end...\n"); + /* ring finishing */ + m->event |= MDMEVENT_RING_CHECK; + if (m->ring_count <= 1) + m->ring_count = duration * freq / 1000 ; + m->ring_last = get_time(); + } + else + MODEM_ERR("RD returns %ld freq. (duration %ld)\n", + freq, duration); + } +#ifdef MODEM_CONFIG_CID + if(m->cid) + modem_cid_process(m, in, out, count); +#endif +} + +int modem_ring_detector_start(struct modem *m) +{ + MODEM_DBG("modem_ring_detector_start...\n"); + if(m->rd_obj) { + MODEM_ERR("modem_ring_detector_start: rd_obj already exists!\n"); + return -1; + } + m->rd_obj = RD_create(m, m->srate); + m->process = modem_ring_detector_process; + modem_set_hook(m, MODEM_HOOK_SNOOPING); + return do_modem_start(m); +} +#endif /* MODEM_CONFIG_RING_DETECTOR */ + + +/* + * Voice modem + * + */ + +#ifdef MODEM_CONFIG_VOICE +extern void modem_voice_stop(struct modem *m); + +static void modem_voice_process(struct modem *m, void *in, void *out, int count) +{ + int status; + //MODEM_DBG("modem_voice_process: %d...\n", count); + status = VOICE_process(m->voice_obj, in, out, count); + if(status) { + MODEM_DBG("modem_voice_process: status %d\n", status); + switch(status) { + case VOICE_STATUS_OK: + modem_report_result(m, RESULT_OK); + m->command = 1; + break; + case VOICE_STATUS_CONNECT: + modem_report_result(m, RESULT_CONNECT); + m->command = 0; + break; + case VOICE_STATUS_ERROR: + modem_report_result(m, RESULT_ERROR); + m->command = 1; + break; + default: + break; + } + if(status < 0) { + MODEM_DBG("VOICE failed.\n"); + modem_voice_stop(m); + } + } +} + +int modem_voice_command(struct modem *m, enum VOICE_CMD cmd) +{ + MODEM_DBG("modem_voice_command: %u...\n", cmd); + switch(cmd) { + case VOICE_CMD_BEEP: + case VOICE_CMD_DTMF: + case VOICE_CMD_STATE_RX: + case VOICE_CMD_STATE_TX: + if(!m->voice_obj) + return -1; + m->command = 0; + VOICE_command(m->voice_obj, cmd); + return 1; + default: + return -1; + } + return 0; +} + +int modem_voice_start(struct modem *m) +{ + MODEM_DBG("modem_voice_start...\n"); + if(m->voice_obj) + return 0; + if(m->started) { +#ifdef MODEM_CONFIG_RING_DETECTOR + if(m->rd_obj) + modem_stop(m); + else +#endif + return -1; + } + m->voice_obj = VOICE_create(m, m->srate); + if(!m->voice_obj) + return -1; + m->sample_timer = 0 /* m->srate*timeout/MODEM_HZ */ ; + m->sample_timer_func = NULL /* modem_voice_stop */ ; + m->process = modem_voice_process; + modem_set_hook(m, MODEM_HOOK_OFF); + return do_modem_start(m); +} + +void modem_voice_stop(struct modem *m) +{ + MODEM_DBG("modem_voice_stop...\n"); + m->process = NULL; + m->sample_timer = 0; + m->sample_timer_func = NULL; + if(m->voice_obj) { + VOICE_delete(m->voice_obj); + m->voice_obj = NULL; + } + modem_stop(m); + modem_set_hook(m, MODEM_HOOK_ON); +} + +int modem_voice_set_device(struct modem *m, unsigned device) +{ + MODEM_DBG("modem_voice_set_device: dev = %u...\n", device); + if (device == VOICE_DEVICE_NONE) { + if(m->voice_obj) + modem_voice_stop(m); + return 0; + } + else if (device == VOICE_DEVICE_LINE) { + if(m->voice_obj) + return 0; + else if(modem_voice_start(m)) + return -1; + return 1; + } + return -1; +} + +int modem_voice_init(struct modem *m) +{ + m->voice_info.comp_method = 1; + m->voice_info.sample_rate = 8000; + m->voice_info.rx_gain = 128; + m->voice_info.tx_gain = 128; + m->voice_info.dtmf_symbol = 0; + m->voice_info.tone1_freq = 933; + m->voice_info.tone2_freq = 0; + m->voice_info.tone_duration = 150; + m->voice_info.inactivity_timer = 0; + m->voice_info.silence_detect_sensitivity = 128; + m->voice_info.silence_detect_period = 50; + return 0; +} + +#endif /* MODEM_CONFIG_VOICE */ + + +/* + * Fax modem + * + */ + +#ifdef MODEM_CONFIG_FAX + +static void modem_fax_process(struct modem *m, void *in, void *out, int count) +{ + int ret; + //MODEM_DBG("modem_fax_process: %d...\n", count); + ret = FAX_process(m->fax_obj, in, out, count); + if(ret) { + MODEM_DBG("fax_process: %d\n", ret); + switch(ret) { + case FAX_STATUS_OK: + modem_set_state(m, STATE_COMMAND_ONLINE); + modem_report_result(m, RESULT_OK); + m->command = 1; + break; + case FAX_STATUS_CONNECT: + modem_set_state(m,STATE_MODEM_ONLINE); + modem_report_result(m, RESULT_CONNECT); + m->command = 0; + break; + case FAX_STATUS_NOCARRIER: + modem_set_state(m, STATE_COMMAND_ONLINE); + modem_report_result(m, RESULT_NOCARRIER); + m->command = 1; + break; + case FAX_STATUS_ERROR: + modem_update_status(m, STATUS_ERROR); + break; + default: + break; + } + if(ret < 0) { + MODEM_DBG("FAX failed.\n"); + modem_stop(m); + } + } +} + +static int modem_fax_start(struct modem *m) +{ + MODEM_DBG("modem_fax_start...\n"); + m->fax_obj = FAX_create(m, m->caller, m->srate); + if(!m->fax_obj) + return -1; + m->sample_timer = 0; + m->sample_timer_func = NULL; + m->process = modem_fax_process; + return 0; +} + +#endif /* MODEM_CONFIG_FAX */ + + +/* + * Commands + * + */ + +int modem_send_to_tty(struct modem *m, const char *buf, int n) +{ + return modem_put_chars(m, buf, n); +} + +int modem_recv_from_tty(struct modem *m, char *buf, int n) +{ + return modem_get_chars(m, buf, n); +} + +int modem_answer(struct modem *m) +{ + MODEM_DBG("modem answer...\n"); + if ( m->dp ) { + MODEM_ERR("dp %d is already exists.\n", m->dp->id); + return -1; + } + if(m->started) + modem_stop(m); + m->dp_requested = 0; + m->automode_requested = 0; /* MODEM_AUTOMODE(m) */ + m->sample_timer = ANSWER_DELAY(m) ? + ANSWER_DELAY(m)*m->srate : m->srate/100; + m->sample_timer_func = do_modem_change_dp; + return modem_start(m); +} + + +static int modem_dial_start(struct modem *m) +{ + struct dp_operations *op; + struct dp *dp = NULL; + MODEM_DBG("modem_dial_start...\n"); + if(m->dp) { + return -1; + } + if (m->started) + modem_stop(m); + m->caller = 1; + op = get_dp_operations(DP_CALLPROG); + if (op && op->create) + dp = op->create(m,DP_CALLPROG, + m->caller,m->srate, + m->frag,op); + if (!dp) { + MODEM_ERR("cannot create CALLPROG.\n"); + return -1; + } + m->dp = dp; + m->process = modem_dp_process; + return 0; +} + +int modem_dial(struct modem *m) +{ + int ret; + MODEM_DBG("modem dial: %s...\n", m->dial_string); + m->dp_requested = 0; + m->automode_requested = 0; + ret = modem_dial_start(m); + if(ret) + return -1; + return modem_start(m); +} + + +int modem_hook(struct modem *m, unsigned hook_state) +{ + MODEM_DBG("modem hook...\n"); + if ( m->hook == hook_state ) + return 0; + if (!IS_STATE_IDLE(m->state)) + modem_hup(m,1); + return modem_set_hook(m,hook_state); +} + +int modem_online(struct modem *m) +{ + MODEM_DBG("modem online...\n"); + if (m->state != STATE_COMMAND_ONLINE) + return -1; + m->command = 0; + modem_set_state(m, STATE_MODEM_ONLINE); + modem_report_result(m,RESULT_CONNECT); // fixme + return 0; +} + +void modem_update_speaker(struct modem *m) +{ + //MODEM_DBG("modem update speaker...\n"); + m->driver.ioctl(m,MDMCTL_SPEAKERVOL, + SPEAKER_CONTROL(m) == 2 ? SPEAKER_VOLUME(m):0); +} + +int modem_get_sreg(struct modem *m, unsigned int num) +{ + if (num >= sizeof(m->sregs)) + return -1; + return m->sregs[num]; +} + +int modem_set_sreg(struct modem *m, unsigned int num, int val) +{ + if (num >= sizeof(m->sregs)) + return -1; + m->sregs[num] = val; + return 0; +} + +/* homolog set initialization */ +int modem_homolog_init(struct modem *m, int id, const char *name) +{ + const struct homolog_set *set; + for(set=homolog_set;set->name;set++) { + if(set->id == id || + (name && *name && !strcmp(name,set->name))) { + m->homolog = set; + modem_set_sreg(m,SREG_DIAL_PAUSE_TIME, + set->params->DialPauseTime); + modem_set_sreg(m,SREG_FLASH_TIMER, + set->params->HookFlashTime); + return 0; + } + } + return -1; +} + + +int modem_set_mode(struct modem *m, enum MODEM_MODE mode) +{ + MODEM_DBG("modem set mode: -> %d...\n", mode); + if (m->mode == mode) + return 0; +#ifdef MODEM_CONFIG_VOICE + if (m->mode == MODEM_MODE_VOICE && m->voice_obj) { + modem_voice_stop(m); + } +#endif + m->mode = mode; + return 0; +} + + +int modem_reset(struct modem *m) +{ + static int sregs_init(unsigned char sregs[]); + MODEM_DBG("modem reset...\n"); + if(m->state != STATE_MODEM_IDLE) + modem_hup(m,1); + else if(m->started) + modem_stop(m); + else if(m->hook) + modem_set_hook(m,MODEM_HOOK_ON); + modem_set_state(m, STATE_MODEM_IDLE); + m->command = 1; + m->min_rate = MODEM_MIN_RATE; + m->max_rate = MODEM_MAX_RATE; + sregs_init(m->sregs); + modem_homolog_init(m,m->homolog->id,NULL); + modem_set_mode(m, MODEM_MODE_DATA); + return 0; +} + + +/* + * Init functions + * + */ + +/* set default init values */ +static int sregs_init(unsigned char sregs[]) +{ + sregs[SREG_ESCAPE_CHAR] = '+' ; /* escape char */ + sregs[SREG_CR_CHAR] = '\r'; /* cr char */ + sregs[SREG_LF_CHAR] = '\n'; /* lf char */ + sregs[SREG_BS_CHAR] = '\b'; /* bs char */ + sregs[SREG_DIAL_TONE_WAIT_TIME] = 2; /* seconds */ + sregs[SREG_WAIT_CARRIER_AFTER_DIAL] = 60; /* seconds */ + sregs[SREG_DIAL_PAUSE_TIME] = 2; /* seconds */ + sregs[SREG_CARRIER_DETECT_RESPONSE_TIME] = 6; /* 0.1 sec */ + sregs[SREG_CARRIER_LOSS_DISCONNECT_TIME] = 7; /* 0.1 sec */ + sregs[SREG_DTMF_DURATION] = 100; /* ms */ + sregs[SREG_ESCAPE_PROMPT_DELAY] = 50; /* ms */ + sregs[SREG_FLASH_TIMER] = 20; /* 10ms */ + sregs[SREG_ECHO] = 1; /* yes */ + sregs[SREG_QUIET] = 0; /* no */ + sregs[SREG_VERBOSE] = 1; /* yes */ + sregs[SREG_TONE_OR_PULSE] = 1; /* tone */ + + sregs[SREG_X_CODE] = 4; + + sregs[SREG_SPEAKER_CONTROL] = 1; /* yes */ + sregs[SREG_SPEAKER_VOLUME] = 3; /* max */ + sregs[SREG_AUTOMODE] = 1; /* yes */ + sregs[SREG_DP] = DP_V92; + + sregs[SREG_ANS_DELAY] = 2; /* seconds */ + + sregs[SREG_LINE_QUALITY_CONTROL] = 0; + sregs[SREG_CD] = 0; + sregs[SREG_FLOW_CONTROL] = 0; + sregs[SREG_CONNNECT_MSG_FORMAT] = 0; + sregs[SREG_CONNNECT_MSG_SPEED_SRC] = 0; + + /* new sregs */ + sregs[SREG_EC] = 1; + sregs[SREG_COMP] = 0x3; + + return 0; +} + + + +/* ---------------------------------------------------------------- */ + +void modem_hangup(struct modem *m) +{ + modem_hup(m,1); +} + + +void modem_update_termios(struct modem *m, struct termios *tios) +{ + MODEM_DBG("update termios...\n"); + if( cfgetispeed(tios) == B0 || + cfgetospeed(tios) == B0 ) { + MODEM_DBG("modem_update_termios: hangup.\n"); + if(m->state!=STATE_MODEM_IDLE) + modem_hup(m,1); + // TBD: drop DTR? + } + else + m->modem_info |= TIOCM_DTR; + m->termios = *tios; +} + + +/* + * + * modem_write() + * + */ + + +static int modem_check_escape(struct modem *m, const char *buf, int count) +{ + unsigned long now = get_time(); + int i; + if(count + m->escape_count > 3 ) + goto noescape_out; + for( i = 0 ; i < count ; i++) { + if(buf[i] != ESCAPE_CHAR(m)) + goto noescape_out; + } + + if(m->escape_count == 0) { + if(time_before(now, m->last_esc_check + ESCAPE_TIMEOUT(m))) + goto noescape_out; + } + else if (time_after(now, m->last_esc_check + ESCAPE_TIMEOUT(m))) + goto noescape_out; + + m->escape_count += count; + if(m->escape_count == 3) + schedule_event(m,MDMEVENT_ESCAPE,ESCAPE_TIMEOUT(m)); + + m->last_esc_check = now; + return m->escape_count; + + noescape_out: + m->escape_count = 0; + m->last_esc_check = now; + return 0; +} + + + +int modem_write(struct modem *m, const char *buffer, int count) +{ + const char *buf; + int cnt, ret = 0; + if(IS_STATE_CONNECTING(m->state)) { + MODEM_DBG("modem_tty_write: hangup...\n"); + modem_hup(m,1); + return count; + } + + if(m->state == STATE_MODEM_ONLINE) + modem_check_escape(m,buffer,count); + + while(count) { + cnt = count; + if(cnt > m->xmit.size - m->xmit.count) + cnt = m->xmit.size - m->xmit.count; + if(cnt > m->xmit.size - m->xmit.head) + cnt = m->xmit.size - m->xmit.head; + if(cnt <= 0) { + MODEM_DBG("modem_write: overflow!\n"); + break; + } + buf = buffer; + memcpy(m->xmit.buf + m->xmit.head, buf, cnt); + m->xmit.count += cnt; + m->xmit.head = (m->xmit.head + cnt)%m->xmit.size; + ret += cnt; + buffer += cnt; + count -= cnt; + } + if(m->command) + modem_at_process(m); + return ret; +} + + + +void modem_print_version() +{ + MODEM_INFO("%s: version %s %s\n", + modem_name,modem_version,modem_date); +} + + +struct modem *modem_create(struct modem_driver *drv, const char *name) +{ + struct modem *m; + + modem_print_version(); + + m = malloc(sizeof(*m)); + if (!m) + return NULL; + memset(m,0,sizeof(*m)); + + m->name = name; + m->driver = *drv; + m->modem_info = 0; + m->state = STATE_MODEM_IDLE; + m->command= 1; + m->hook = MODEM_HOOK_ON; + m->caller = 0; + m->min_rate = MODEM_MIN_RATE; + m->max_rate = MODEM_MAX_RATE; + + m->modem_info |= (TIOCM_DSR|TIOCM_CTS); + + sregs_init(m->sregs); + + if(modem_homolog_init(m, MODEM_DEFAULT_COUNTRY_CODE, NULL)) { + MODEM_ERR("bad default country code `%x'!\n", + MODEM_DEFAULT_COUNTRY_CODE); + free(m); + return NULL; + } + if(modem_default_country && + modem_homolog_init(m, -1, modem_default_country)) { + MODEM_INFO("bad country name `%s', using default by code!\n", + modem_default_country); + } + + m->ring_last = get_time(); + + timer_init(&m->event_timer); + + /* setup config */ + modem_setup_config(m); + /* packer initializations */ + // fixme: TBD + + /* dp initialization */ + m->format = MODEM_FORMAT; + m->srate = MODEM_RATE; + m->frag = MODEM_FRAG; /* in samples */ + m->dp = NULL; + + MODEM_DBG("startup modem...\n"); + m->xmit.buf = malloc(XMIT_SIZE); + if ( !m->xmit.buf ) { + free(m); + return NULL; + } + m->xmit.size = XMIT_SIZE; + m->xmit.head = m->xmit.tail = m->xmit.count = 0; + m->modem_info |= TIOCM_DTR|TIOCM_RTS; + // TODO: update speed,DTR according to termios +#ifdef MODEM_CONFIG_VOICE + modem_voice_init(m); +#endif + + return m; +} + + + +void modem_delete(struct modem *m) +{ + MODEM_DBG("modem_delete...\n"); + if(m->started) { + MODEM_DBG("shutdown modem...\n"); + if(m->state != STATE_MODEM_IDLE) + m->result_code = RESULT_NOCARRIER; + modem_stop(m); + } + m->xmit.size = 0; + m->xmit.count = m->xmit.tail = m->xmit.head = 0; + free(m->xmit.buf); + m->xmit.buf = 0; + + timer_del(&m->event_timer); + free(m); +} + + + +