1282361a0SGreg Kroah-Hartman /* 2282361a0SGreg Kroah-Hartman * IPWireless 3G PCMCIA Network Driver 3282361a0SGreg Kroah-Hartman * 4282361a0SGreg Kroah-Hartman * Original code 5282361a0SGreg Kroah-Hartman * by Stephen Blackheath <stephen@blacksapphire.com>, 6282361a0SGreg Kroah-Hartman * Ben Martel <benm@symmetric.co.nz> 7282361a0SGreg Kroah-Hartman * 8282361a0SGreg Kroah-Hartman * Copyrighted as follows: 9282361a0SGreg Kroah-Hartman * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) 10282361a0SGreg Kroah-Hartman * 11282361a0SGreg Kroah-Hartman * Various driver changes and rewrites, port to new kernels 12282361a0SGreg Kroah-Hartman * Copyright (C) 2006-2007 Jiri Kosina 13282361a0SGreg Kroah-Hartman * 14282361a0SGreg Kroah-Hartman * Misc code cleanups and updates 15282361a0SGreg Kroah-Hartman * Copyright (C) 2007 David Sterba 16282361a0SGreg Kroah-Hartman */ 17282361a0SGreg Kroah-Hartman 18282361a0SGreg Kroah-Hartman #include <linux/init.h> 19282361a0SGreg Kroah-Hartman #include <linux/kernel.h> 20282361a0SGreg Kroah-Hartman #include <linux/module.h> 21282361a0SGreg Kroah-Hartman #include <linux/mutex.h> 22282361a0SGreg Kroah-Hartman #include <linux/ppp_defs.h> 23282361a0SGreg Kroah-Hartman #include <linux/if.h> 244b32da2bSPaul Mackerras #include <linux/ppp-ioctl.h> 25282361a0SGreg Kroah-Hartman #include <linux/sched.h> 26282361a0SGreg Kroah-Hartman #include <linux/serial.h> 27282361a0SGreg Kroah-Hartman #include <linux/slab.h> 28282361a0SGreg Kroah-Hartman #include <linux/tty.h> 29282361a0SGreg Kroah-Hartman #include <linux/tty_driver.h> 30282361a0SGreg Kroah-Hartman #include <linux/tty_flip.h> 31282361a0SGreg Kroah-Hartman #include <linux/uaccess.h> 32282361a0SGreg Kroah-Hartman 33282361a0SGreg Kroah-Hartman #include "tty.h" 34282361a0SGreg Kroah-Hartman #include "network.h" 35282361a0SGreg Kroah-Hartman #include "hardware.h" 36282361a0SGreg Kroah-Hartman #include "main.h" 37282361a0SGreg Kroah-Hartman 38282361a0SGreg Kroah-Hartman #define IPWIRELESS_PCMCIA_START (0) 39282361a0SGreg Kroah-Hartman #define IPWIRELESS_PCMCIA_MINORS (24) 40282361a0SGreg Kroah-Hartman #define IPWIRELESS_PCMCIA_MINOR_RANGE (8) 41282361a0SGreg Kroah-Hartman 42282361a0SGreg Kroah-Hartman #define TTYTYPE_MODEM (0) 43282361a0SGreg Kroah-Hartman #define TTYTYPE_MONITOR (1) 44282361a0SGreg Kroah-Hartman #define TTYTYPE_RAS_RAW (2) 45282361a0SGreg Kroah-Hartman 46282361a0SGreg Kroah-Hartman struct ipw_tty { 477393af80SJiri Slaby struct tty_port port; 48282361a0SGreg Kroah-Hartman int index; 49282361a0SGreg Kroah-Hartman struct ipw_hardware *hardware; 50282361a0SGreg Kroah-Hartman unsigned int channel_idx; 51282361a0SGreg Kroah-Hartman unsigned int secondary_channel_idx; 52282361a0SGreg Kroah-Hartman int tty_type; 53282361a0SGreg Kroah-Hartman struct ipw_network *network; 54282361a0SGreg Kroah-Hartman unsigned int control_lines; 55282361a0SGreg Kroah-Hartman struct mutex ipw_tty_mutex; 56282361a0SGreg Kroah-Hartman int tx_bytes_queued; 57282361a0SGreg Kroah-Hartman int closing; 58282361a0SGreg Kroah-Hartman }; 59282361a0SGreg Kroah-Hartman 60282361a0SGreg Kroah-Hartman static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS]; 61282361a0SGreg Kroah-Hartman 62282361a0SGreg Kroah-Hartman static struct tty_driver *ipw_tty_driver; 63282361a0SGreg Kroah-Hartman 64282361a0SGreg Kroah-Hartman static char *tty_type_name(int tty_type) 65282361a0SGreg Kroah-Hartman { 66282361a0SGreg Kroah-Hartman static char *channel_names[] = { 67282361a0SGreg Kroah-Hartman "modem", 68282361a0SGreg Kroah-Hartman "monitor", 69282361a0SGreg Kroah-Hartman "RAS-raw" 70282361a0SGreg Kroah-Hartman }; 71282361a0SGreg Kroah-Hartman 72282361a0SGreg Kroah-Hartman return channel_names[tty_type]; 73282361a0SGreg Kroah-Hartman } 74282361a0SGreg Kroah-Hartman 75ecaa3bdaSJiri Slaby static struct ipw_tty *get_tty(int index) 76282361a0SGreg Kroah-Hartman { 77282361a0SGreg Kroah-Hartman /* 78282361a0SGreg Kroah-Hartman * The 'ras_raw' channel is only available when 'loopback' mode 79282361a0SGreg Kroah-Hartman * is enabled. 80282361a0SGreg Kroah-Hartman * Number of minor starts with 16 (_RANGE * _RAS_RAW). 81282361a0SGreg Kroah-Hartman */ 82ecaa3bdaSJiri Slaby if (!ipwireless_loopback && index >= 83282361a0SGreg Kroah-Hartman IPWIRELESS_PCMCIA_MINOR_RANGE * TTYTYPE_RAS_RAW) 84282361a0SGreg Kroah-Hartman return NULL; 85282361a0SGreg Kroah-Hartman 86ecaa3bdaSJiri Slaby return ttys[index]; 87282361a0SGreg Kroah-Hartman } 88282361a0SGreg Kroah-Hartman 89282361a0SGreg Kroah-Hartman static int ipw_open(struct tty_struct *linux_tty, struct file *filp) 90282361a0SGreg Kroah-Hartman { 91ecaa3bdaSJiri Slaby struct ipw_tty *tty = get_tty(linux_tty->index); 92282361a0SGreg Kroah-Hartman 93282361a0SGreg Kroah-Hartman if (!tty) 94282361a0SGreg Kroah-Hartman return -ENODEV; 95282361a0SGreg Kroah-Hartman 96282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex); 97282361a0SGreg Kroah-Hartman 98282361a0SGreg Kroah-Hartman if (tty->closing) { 99282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 100282361a0SGreg Kroah-Hartman return -ENODEV; 101282361a0SGreg Kroah-Hartman } 1027393af80SJiri Slaby if (tty->port.count == 0) 103282361a0SGreg Kroah-Hartman tty->tx_bytes_queued = 0; 104282361a0SGreg Kroah-Hartman 1057393af80SJiri Slaby tty->port.count++; 106282361a0SGreg Kroah-Hartman 10719ef1b71SJiri Slaby tty->port.tty = linux_tty; 108282361a0SGreg Kroah-Hartman linux_tty->driver_data = tty; 109282361a0SGreg Kroah-Hartman linux_tty->low_latency = 1; 110282361a0SGreg Kroah-Hartman 111282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM) 112282361a0SGreg Kroah-Hartman ipwireless_ppp_open(tty->network); 113282361a0SGreg Kroah-Hartman 114282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 115282361a0SGreg Kroah-Hartman 116282361a0SGreg Kroah-Hartman return 0; 117282361a0SGreg Kroah-Hartman } 118282361a0SGreg Kroah-Hartman 119282361a0SGreg Kroah-Hartman static void do_ipw_close(struct ipw_tty *tty) 120282361a0SGreg Kroah-Hartman { 1217393af80SJiri Slaby tty->port.count--; 122282361a0SGreg Kroah-Hartman 1237393af80SJiri Slaby if (tty->port.count == 0) { 12419ef1b71SJiri Slaby struct tty_struct *linux_tty = tty->port.tty; 125282361a0SGreg Kroah-Hartman 126282361a0SGreg Kroah-Hartman if (linux_tty != NULL) { 12719ef1b71SJiri Slaby tty->port.tty = NULL; 128282361a0SGreg Kroah-Hartman linux_tty->driver_data = NULL; 129282361a0SGreg Kroah-Hartman 130282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM) 131282361a0SGreg Kroah-Hartman ipwireless_ppp_close(tty->network); 132282361a0SGreg Kroah-Hartman } 133282361a0SGreg Kroah-Hartman } 134282361a0SGreg Kroah-Hartman } 135282361a0SGreg Kroah-Hartman 136282361a0SGreg Kroah-Hartman static void ipw_hangup(struct tty_struct *linux_tty) 137282361a0SGreg Kroah-Hartman { 138282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 139282361a0SGreg Kroah-Hartman 140282361a0SGreg Kroah-Hartman if (!tty) 141282361a0SGreg Kroah-Hartman return; 142282361a0SGreg Kroah-Hartman 143282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex); 1447393af80SJiri Slaby if (tty->port.count == 0) { 145282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 146282361a0SGreg Kroah-Hartman return; 147282361a0SGreg Kroah-Hartman } 148282361a0SGreg Kroah-Hartman 149282361a0SGreg Kroah-Hartman do_ipw_close(tty); 150282361a0SGreg Kroah-Hartman 151282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 152282361a0SGreg Kroah-Hartman } 153282361a0SGreg Kroah-Hartman 154282361a0SGreg Kroah-Hartman static void ipw_close(struct tty_struct *linux_tty, struct file *filp) 155282361a0SGreg Kroah-Hartman { 156282361a0SGreg Kroah-Hartman ipw_hangup(linux_tty); 157282361a0SGreg Kroah-Hartman } 158282361a0SGreg Kroah-Hartman 159282361a0SGreg Kroah-Hartman /* Take data received from hardware, and send it out the tty */ 160282361a0SGreg Kroah-Hartman void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, 161282361a0SGreg Kroah-Hartman unsigned int length) 162282361a0SGreg Kroah-Hartman { 163282361a0SGreg Kroah-Hartman struct tty_struct *linux_tty; 164282361a0SGreg Kroah-Hartman int work = 0; 165282361a0SGreg Kroah-Hartman 166282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex); 16719ef1b71SJiri Slaby linux_tty = tty->port.tty; 168282361a0SGreg Kroah-Hartman if (linux_tty == NULL) { 169282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 170282361a0SGreg Kroah-Hartman return; 171282361a0SGreg Kroah-Hartman } 172282361a0SGreg Kroah-Hartman 1737393af80SJiri Slaby if (!tty->port.count) { 174282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 175282361a0SGreg Kroah-Hartman return; 176282361a0SGreg Kroah-Hartman } 177282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 178282361a0SGreg Kroah-Hartman 17905c7cd39SJiri Slaby work = tty_insert_flip_string(&tty->port, data, length); 180282361a0SGreg Kroah-Hartman 181282361a0SGreg Kroah-Hartman if (work != length) 182282361a0SGreg Kroah-Hartman printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME 183282361a0SGreg Kroah-Hartman ": %d chars not inserted to flip buffer!\n", 184282361a0SGreg Kroah-Hartman length - work); 185282361a0SGreg Kroah-Hartman 186282361a0SGreg Kroah-Hartman /* 187282361a0SGreg Kroah-Hartman * This may sleep if ->low_latency is set 188282361a0SGreg Kroah-Hartman */ 189282361a0SGreg Kroah-Hartman if (work) 190282361a0SGreg Kroah-Hartman tty_flip_buffer_push(linux_tty); 191282361a0SGreg Kroah-Hartman } 192282361a0SGreg Kroah-Hartman 193282361a0SGreg Kroah-Hartman static void ipw_write_packet_sent_callback(void *callback_data, 194282361a0SGreg Kroah-Hartman unsigned int packet_length) 195282361a0SGreg Kroah-Hartman { 196282361a0SGreg Kroah-Hartman struct ipw_tty *tty = callback_data; 197282361a0SGreg Kroah-Hartman 198282361a0SGreg Kroah-Hartman /* 199282361a0SGreg Kroah-Hartman * Packet has been sent, so we subtract the number of bytes from our 200282361a0SGreg Kroah-Hartman * tally of outstanding TX bytes. 201282361a0SGreg Kroah-Hartman */ 202282361a0SGreg Kroah-Hartman tty->tx_bytes_queued -= packet_length; 203282361a0SGreg Kroah-Hartman } 204282361a0SGreg Kroah-Hartman 205282361a0SGreg Kroah-Hartman static int ipw_write(struct tty_struct *linux_tty, 206282361a0SGreg Kroah-Hartman const unsigned char *buf, int count) 207282361a0SGreg Kroah-Hartman { 208282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 209282361a0SGreg Kroah-Hartman int room, ret; 210282361a0SGreg Kroah-Hartman 211282361a0SGreg Kroah-Hartman if (!tty) 212282361a0SGreg Kroah-Hartman return -ENODEV; 213282361a0SGreg Kroah-Hartman 214282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex); 2157393af80SJiri Slaby if (!tty->port.count) { 216282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 217282361a0SGreg Kroah-Hartman return -EINVAL; 218282361a0SGreg Kroah-Hartman } 219282361a0SGreg Kroah-Hartman 220282361a0SGreg Kroah-Hartman room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; 221282361a0SGreg Kroah-Hartman if (room < 0) 222282361a0SGreg Kroah-Hartman room = 0; 223282361a0SGreg Kroah-Hartman /* Don't allow caller to write any more than we have room for */ 224282361a0SGreg Kroah-Hartman if (count > room) 225282361a0SGreg Kroah-Hartman count = room; 226282361a0SGreg Kroah-Hartman 227282361a0SGreg Kroah-Hartman if (count == 0) { 228282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 229282361a0SGreg Kroah-Hartman return 0; 230282361a0SGreg Kroah-Hartman } 231282361a0SGreg Kroah-Hartman 232282361a0SGreg Kroah-Hartman ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS, 233282361a0SGreg Kroah-Hartman buf, count, 234282361a0SGreg Kroah-Hartman ipw_write_packet_sent_callback, tty); 235282361a0SGreg Kroah-Hartman if (ret == -1) { 236282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 237282361a0SGreg Kroah-Hartman return 0; 238282361a0SGreg Kroah-Hartman } 239282361a0SGreg Kroah-Hartman 240282361a0SGreg Kroah-Hartman tty->tx_bytes_queued += count; 241282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 242282361a0SGreg Kroah-Hartman 243282361a0SGreg Kroah-Hartman return count; 244282361a0SGreg Kroah-Hartman } 245282361a0SGreg Kroah-Hartman 246282361a0SGreg Kroah-Hartman static int ipw_write_room(struct tty_struct *linux_tty) 247282361a0SGreg Kroah-Hartman { 248282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 249282361a0SGreg Kroah-Hartman int room; 250282361a0SGreg Kroah-Hartman 251282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 252282361a0SGreg Kroah-Hartman if (!tty) 253282361a0SGreg Kroah-Hartman return -ENODEV; 254282361a0SGreg Kroah-Hartman 2557393af80SJiri Slaby if (!tty->port.count) 256282361a0SGreg Kroah-Hartman return -EINVAL; 257282361a0SGreg Kroah-Hartman 258282361a0SGreg Kroah-Hartman room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; 259282361a0SGreg Kroah-Hartman if (room < 0) 260282361a0SGreg Kroah-Hartman room = 0; 261282361a0SGreg Kroah-Hartman 262282361a0SGreg Kroah-Hartman return room; 263282361a0SGreg Kroah-Hartman } 264282361a0SGreg Kroah-Hartman 265282361a0SGreg Kroah-Hartman static int ipwireless_get_serial_info(struct ipw_tty *tty, 266282361a0SGreg Kroah-Hartman struct serial_struct __user *retinfo) 267282361a0SGreg Kroah-Hartman { 268282361a0SGreg Kroah-Hartman struct serial_struct tmp; 269282361a0SGreg Kroah-Hartman 270282361a0SGreg Kroah-Hartman if (!retinfo) 271282361a0SGreg Kroah-Hartman return (-EFAULT); 272282361a0SGreg Kroah-Hartman 273282361a0SGreg Kroah-Hartman memset(&tmp, 0, sizeof(tmp)); 274282361a0SGreg Kroah-Hartman tmp.type = PORT_UNKNOWN; 275282361a0SGreg Kroah-Hartman tmp.line = tty->index; 276282361a0SGreg Kroah-Hartman tmp.port = 0; 277282361a0SGreg Kroah-Hartman tmp.irq = 0; 278282361a0SGreg Kroah-Hartman tmp.flags = 0; 279282361a0SGreg Kroah-Hartman tmp.baud_base = 115200; 280282361a0SGreg Kroah-Hartman tmp.close_delay = 0; 281282361a0SGreg Kroah-Hartman tmp.closing_wait = 0; 282282361a0SGreg Kroah-Hartman tmp.custom_divisor = 0; 283282361a0SGreg Kroah-Hartman tmp.hub6 = 0; 284282361a0SGreg Kroah-Hartman if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) 285282361a0SGreg Kroah-Hartman return -EFAULT; 286282361a0SGreg Kroah-Hartman 287282361a0SGreg Kroah-Hartman return 0; 288282361a0SGreg Kroah-Hartman } 289282361a0SGreg Kroah-Hartman 290282361a0SGreg Kroah-Hartman static int ipw_chars_in_buffer(struct tty_struct *linux_tty) 291282361a0SGreg Kroah-Hartman { 292282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 293282361a0SGreg Kroah-Hartman 294282361a0SGreg Kroah-Hartman if (!tty) 295282361a0SGreg Kroah-Hartman return 0; 296282361a0SGreg Kroah-Hartman 2977393af80SJiri Slaby if (!tty->port.count) 298282361a0SGreg Kroah-Hartman return 0; 299282361a0SGreg Kroah-Hartman 300282361a0SGreg Kroah-Hartman return tty->tx_bytes_queued; 301282361a0SGreg Kroah-Hartman } 302282361a0SGreg Kroah-Hartman 303282361a0SGreg Kroah-Hartman static int get_control_lines(struct ipw_tty *tty) 304282361a0SGreg Kroah-Hartman { 305282361a0SGreg Kroah-Hartman unsigned int my = tty->control_lines; 306282361a0SGreg Kroah-Hartman unsigned int out = 0; 307282361a0SGreg Kroah-Hartman 308282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_RTS) 309282361a0SGreg Kroah-Hartman out |= TIOCM_RTS; 310282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DTR) 311282361a0SGreg Kroah-Hartman out |= TIOCM_DTR; 312282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_CTS) 313282361a0SGreg Kroah-Hartman out |= TIOCM_CTS; 314282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DSR) 315282361a0SGreg Kroah-Hartman out |= TIOCM_DSR; 316282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DCD) 317282361a0SGreg Kroah-Hartman out |= TIOCM_CD; 318282361a0SGreg Kroah-Hartman 319282361a0SGreg Kroah-Hartman return out; 320282361a0SGreg Kroah-Hartman } 321282361a0SGreg Kroah-Hartman 322282361a0SGreg Kroah-Hartman static int set_control_lines(struct ipw_tty *tty, unsigned int set, 323282361a0SGreg Kroah-Hartman unsigned int clear) 324282361a0SGreg Kroah-Hartman { 325282361a0SGreg Kroah-Hartman int ret; 326282361a0SGreg Kroah-Hartman 327282361a0SGreg Kroah-Hartman if (set & TIOCM_RTS) { 328282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1); 329282361a0SGreg Kroah-Hartman if (ret) 330282361a0SGreg Kroah-Hartman return ret; 331282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 332282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, 333282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 1); 334282361a0SGreg Kroah-Hartman if (ret) 335282361a0SGreg Kroah-Hartman return ret; 336282361a0SGreg Kroah-Hartman } 337282361a0SGreg Kroah-Hartman } 338282361a0SGreg Kroah-Hartman if (set & TIOCM_DTR) { 339282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1); 340282361a0SGreg Kroah-Hartman if (ret) 341282361a0SGreg Kroah-Hartman return ret; 342282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 343282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, 344282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 1); 345282361a0SGreg Kroah-Hartman if (ret) 346282361a0SGreg Kroah-Hartman return ret; 347282361a0SGreg Kroah-Hartman } 348282361a0SGreg Kroah-Hartman } 349282361a0SGreg Kroah-Hartman if (clear & TIOCM_RTS) { 350282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0); 351282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 352282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, 353282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 0); 354282361a0SGreg Kroah-Hartman if (ret) 355282361a0SGreg Kroah-Hartman return ret; 356282361a0SGreg Kroah-Hartman } 357282361a0SGreg Kroah-Hartman } 358282361a0SGreg Kroah-Hartman if (clear & TIOCM_DTR) { 359282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0); 360282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 361282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, 362282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 0); 363282361a0SGreg Kroah-Hartman if (ret) 364282361a0SGreg Kroah-Hartman return ret; 365282361a0SGreg Kroah-Hartman } 366282361a0SGreg Kroah-Hartman } 367282361a0SGreg Kroah-Hartman return 0; 368282361a0SGreg Kroah-Hartman } 369282361a0SGreg Kroah-Hartman 370282361a0SGreg Kroah-Hartman static int ipw_tiocmget(struct tty_struct *linux_tty) 371282361a0SGreg Kroah-Hartman { 372282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 373282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 374282361a0SGreg Kroah-Hartman 375282361a0SGreg Kroah-Hartman if (!tty) 376282361a0SGreg Kroah-Hartman return -ENODEV; 377282361a0SGreg Kroah-Hartman 3787393af80SJiri Slaby if (!tty->port.count) 379282361a0SGreg Kroah-Hartman return -EINVAL; 380282361a0SGreg Kroah-Hartman 381282361a0SGreg Kroah-Hartman return get_control_lines(tty); 382282361a0SGreg Kroah-Hartman } 383282361a0SGreg Kroah-Hartman 384282361a0SGreg Kroah-Hartman static int 385282361a0SGreg Kroah-Hartman ipw_tiocmset(struct tty_struct *linux_tty, 386282361a0SGreg Kroah-Hartman unsigned int set, unsigned int clear) 387282361a0SGreg Kroah-Hartman { 388282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 389282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 390282361a0SGreg Kroah-Hartman 391282361a0SGreg Kroah-Hartman if (!tty) 392282361a0SGreg Kroah-Hartman return -ENODEV; 393282361a0SGreg Kroah-Hartman 3947393af80SJiri Slaby if (!tty->port.count) 395282361a0SGreg Kroah-Hartman return -EINVAL; 396282361a0SGreg Kroah-Hartman 397282361a0SGreg Kroah-Hartman return set_control_lines(tty, set, clear); 398282361a0SGreg Kroah-Hartman } 399282361a0SGreg Kroah-Hartman 400282361a0SGreg Kroah-Hartman static int ipw_ioctl(struct tty_struct *linux_tty, 401282361a0SGreg Kroah-Hartman unsigned int cmd, unsigned long arg) 402282361a0SGreg Kroah-Hartman { 403282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 404282361a0SGreg Kroah-Hartman 405282361a0SGreg Kroah-Hartman if (!tty) 406282361a0SGreg Kroah-Hartman return -ENODEV; 407282361a0SGreg Kroah-Hartman 4087393af80SJiri Slaby if (!tty->port.count) 409282361a0SGreg Kroah-Hartman return -EINVAL; 410282361a0SGreg Kroah-Hartman 411282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 412282361a0SGreg Kroah-Hartman 413282361a0SGreg Kroah-Hartman switch (cmd) { 414282361a0SGreg Kroah-Hartman case TIOCGSERIAL: 415282361a0SGreg Kroah-Hartman return ipwireless_get_serial_info(tty, (void __user *) arg); 416282361a0SGreg Kroah-Hartman 417282361a0SGreg Kroah-Hartman case TIOCSSERIAL: 418282361a0SGreg Kroah-Hartman return 0; /* Keeps the PCMCIA scripts happy. */ 419282361a0SGreg Kroah-Hartman } 420282361a0SGreg Kroah-Hartman 421282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM) { 422282361a0SGreg Kroah-Hartman switch (cmd) { 423282361a0SGreg Kroah-Hartman case PPPIOCGCHAN: 424282361a0SGreg Kroah-Hartman { 425282361a0SGreg Kroah-Hartman int chan = ipwireless_ppp_channel_index( 426282361a0SGreg Kroah-Hartman tty->network); 427282361a0SGreg Kroah-Hartman 428282361a0SGreg Kroah-Hartman if (chan < 0) 429282361a0SGreg Kroah-Hartman return -ENODEV; 430282361a0SGreg Kroah-Hartman if (put_user(chan, (int __user *) arg)) 431282361a0SGreg Kroah-Hartman return -EFAULT; 432282361a0SGreg Kroah-Hartman } 433282361a0SGreg Kroah-Hartman return 0; 434282361a0SGreg Kroah-Hartman 435282361a0SGreg Kroah-Hartman case PPPIOCGUNIT: 436282361a0SGreg Kroah-Hartman { 437282361a0SGreg Kroah-Hartman int unit = ipwireless_ppp_unit_number( 438282361a0SGreg Kroah-Hartman tty->network); 439282361a0SGreg Kroah-Hartman 440282361a0SGreg Kroah-Hartman if (unit < 0) 441282361a0SGreg Kroah-Hartman return -ENODEV; 442282361a0SGreg Kroah-Hartman if (put_user(unit, (int __user *) arg)) 443282361a0SGreg Kroah-Hartman return -EFAULT; 444282361a0SGreg Kroah-Hartman } 445282361a0SGreg Kroah-Hartman return 0; 446282361a0SGreg Kroah-Hartman 447282361a0SGreg Kroah-Hartman case FIONREAD: 448282361a0SGreg Kroah-Hartman { 449282361a0SGreg Kroah-Hartman int val = 0; 450282361a0SGreg Kroah-Hartman 451282361a0SGreg Kroah-Hartman if (put_user(val, (int __user *) arg)) 452282361a0SGreg Kroah-Hartman return -EFAULT; 453282361a0SGreg Kroah-Hartman } 454282361a0SGreg Kroah-Hartman return 0; 455282361a0SGreg Kroah-Hartman case TCFLSH: 456282361a0SGreg Kroah-Hartman return tty_perform_flush(linux_tty, arg); 457282361a0SGreg Kroah-Hartman } 458282361a0SGreg Kroah-Hartman } 459282361a0SGreg Kroah-Hartman return -ENOIOCTLCMD; 460282361a0SGreg Kroah-Hartman } 461282361a0SGreg Kroah-Hartman 462282361a0SGreg Kroah-Hartman static int add_tty(int j, 463282361a0SGreg Kroah-Hartman struct ipw_hardware *hardware, 464282361a0SGreg Kroah-Hartman struct ipw_network *network, int channel_idx, 465282361a0SGreg Kroah-Hartman int secondary_channel_idx, int tty_type) 466282361a0SGreg Kroah-Hartman { 467282361a0SGreg Kroah-Hartman ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL); 468282361a0SGreg Kroah-Hartman if (!ttys[j]) 469282361a0SGreg Kroah-Hartman return -ENOMEM; 470282361a0SGreg Kroah-Hartman ttys[j]->index = j; 471282361a0SGreg Kroah-Hartman ttys[j]->hardware = hardware; 472282361a0SGreg Kroah-Hartman ttys[j]->channel_idx = channel_idx; 473282361a0SGreg Kroah-Hartman ttys[j]->secondary_channel_idx = secondary_channel_idx; 474282361a0SGreg Kroah-Hartman ttys[j]->network = network; 475282361a0SGreg Kroah-Hartman ttys[j]->tty_type = tty_type; 476282361a0SGreg Kroah-Hartman mutex_init(&ttys[j]->ipw_tty_mutex); 4777393af80SJiri Slaby tty_port_init(&ttys[j]->port); 478282361a0SGreg Kroah-Hartman 479734cc178SJiri Slaby tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL); 480282361a0SGreg Kroah-Hartman ipwireless_associate_network_tty(network, channel_idx, ttys[j]); 481282361a0SGreg Kroah-Hartman 482282361a0SGreg Kroah-Hartman if (secondary_channel_idx != -1) 483282361a0SGreg Kroah-Hartman ipwireless_associate_network_tty(network, 484282361a0SGreg Kroah-Hartman secondary_channel_idx, 485282361a0SGreg Kroah-Hartman ttys[j]); 486e6df3cceSJiri Slaby /* check if we provide raw device (if loopback is enabled) */ 487e6df3cceSJiri Slaby if (get_tty(j)) 488e6df3cceSJiri Slaby printk(KERN_INFO IPWIRELESS_PCCARD_NAME 489e6df3cceSJiri Slaby ": registering %s device ttyIPWp%d\n", 490e6df3cceSJiri Slaby tty_type_name(tty_type), j); 491e6df3cceSJiri Slaby 492282361a0SGreg Kroah-Hartman return 0; 493282361a0SGreg Kroah-Hartman } 494282361a0SGreg Kroah-Hartman 495282361a0SGreg Kroah-Hartman struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, 496282361a0SGreg Kroah-Hartman struct ipw_network *network) 497282361a0SGreg Kroah-Hartman { 498282361a0SGreg Kroah-Hartman int i, j; 499282361a0SGreg Kroah-Hartman 500282361a0SGreg Kroah-Hartman for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) { 501282361a0SGreg Kroah-Hartman int allfree = 1; 502282361a0SGreg Kroah-Hartman 503282361a0SGreg Kroah-Hartman for (j = i; j < IPWIRELESS_PCMCIA_MINORS; 504282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE) 505282361a0SGreg Kroah-Hartman if (ttys[j] != NULL) { 506282361a0SGreg Kroah-Hartman allfree = 0; 507282361a0SGreg Kroah-Hartman break; 508282361a0SGreg Kroah-Hartman } 509282361a0SGreg Kroah-Hartman 510282361a0SGreg Kroah-Hartman if (allfree) { 511282361a0SGreg Kroah-Hartman j = i; 512282361a0SGreg Kroah-Hartman 513282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network, 514282361a0SGreg Kroah-Hartman IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS, 515282361a0SGreg Kroah-Hartman TTYTYPE_MODEM)) 516282361a0SGreg Kroah-Hartman return NULL; 517282361a0SGreg Kroah-Hartman 518282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE; 519282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network, 520282361a0SGreg Kroah-Hartman IPW_CHANNEL_DIALLER, -1, 521282361a0SGreg Kroah-Hartman TTYTYPE_MONITOR)) 522282361a0SGreg Kroah-Hartman return NULL; 523282361a0SGreg Kroah-Hartman 524282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE; 525282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network, 526282361a0SGreg Kroah-Hartman IPW_CHANNEL_RAS, -1, 527282361a0SGreg Kroah-Hartman TTYTYPE_RAS_RAW)) 528282361a0SGreg Kroah-Hartman return NULL; 529282361a0SGreg Kroah-Hartman 530282361a0SGreg Kroah-Hartman return ttys[i]; 531282361a0SGreg Kroah-Hartman } 532282361a0SGreg Kroah-Hartman } 533282361a0SGreg Kroah-Hartman return NULL; 534282361a0SGreg Kroah-Hartman } 535282361a0SGreg Kroah-Hartman 536282361a0SGreg Kroah-Hartman /* 537282361a0SGreg Kroah-Hartman * Must be called before ipwireless_network_free(). 538282361a0SGreg Kroah-Hartman */ 539282361a0SGreg Kroah-Hartman void ipwireless_tty_free(struct ipw_tty *tty) 540282361a0SGreg Kroah-Hartman { 541282361a0SGreg Kroah-Hartman int j; 542282361a0SGreg Kroah-Hartman struct ipw_network *network = ttys[tty->index]->network; 543282361a0SGreg Kroah-Hartman 544282361a0SGreg Kroah-Hartman for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS; 545282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE) { 546282361a0SGreg Kroah-Hartman struct ipw_tty *ttyj = ttys[j]; 547282361a0SGreg Kroah-Hartman 548282361a0SGreg Kroah-Hartman if (ttyj) { 549282361a0SGreg Kroah-Hartman mutex_lock(&ttyj->ipw_tty_mutex); 550e6df3cceSJiri Slaby if (get_tty(j)) 551e6df3cceSJiri Slaby printk(KERN_INFO IPWIRELESS_PCCARD_NAME 552e6df3cceSJiri Slaby ": deregistering %s device ttyIPWp%d\n", 553e6df3cceSJiri Slaby tty_type_name(ttyj->tty_type), j); 554282361a0SGreg Kroah-Hartman ttyj->closing = 1; 55519ef1b71SJiri Slaby if (ttyj->port.tty != NULL) { 556282361a0SGreg Kroah-Hartman mutex_unlock(&ttyj->ipw_tty_mutex); 55719ef1b71SJiri Slaby tty_vhangup(ttyj->port.tty); 558282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here 559282361a0SGreg Kroah-Hartman against a parallel ioctl etc */ 560de3a60a3SJiri Slaby /* FIXME2: hangup does not mean all processes 561de3a60a3SJiri Slaby * are gone */ 562282361a0SGreg Kroah-Hartman mutex_lock(&ttyj->ipw_tty_mutex); 563282361a0SGreg Kroah-Hartman } 5647393af80SJiri Slaby while (ttyj->port.count) 565282361a0SGreg Kroah-Hartman do_ipw_close(ttyj); 566282361a0SGreg Kroah-Hartman ipwireless_disassociate_network_ttys(network, 567282361a0SGreg Kroah-Hartman ttyj->channel_idx); 568282361a0SGreg Kroah-Hartman tty_unregister_device(ipw_tty_driver, j); 569191c5f10SJiri Slaby tty_port_destroy(&ttyj->port); 570282361a0SGreg Kroah-Hartman ttys[j] = NULL; 571282361a0SGreg Kroah-Hartman mutex_unlock(&ttyj->ipw_tty_mutex); 572282361a0SGreg Kroah-Hartman kfree(ttyj); 573282361a0SGreg Kroah-Hartman } 574282361a0SGreg Kroah-Hartman } 575282361a0SGreg Kroah-Hartman } 576282361a0SGreg Kroah-Hartman 577282361a0SGreg Kroah-Hartman static const struct tty_operations tty_ops = { 578282361a0SGreg Kroah-Hartman .open = ipw_open, 579282361a0SGreg Kroah-Hartman .close = ipw_close, 580282361a0SGreg Kroah-Hartman .hangup = ipw_hangup, 581282361a0SGreg Kroah-Hartman .write = ipw_write, 582282361a0SGreg Kroah-Hartman .write_room = ipw_write_room, 583282361a0SGreg Kroah-Hartman .ioctl = ipw_ioctl, 584282361a0SGreg Kroah-Hartman .chars_in_buffer = ipw_chars_in_buffer, 585282361a0SGreg Kroah-Hartman .tiocmget = ipw_tiocmget, 586282361a0SGreg Kroah-Hartman .tiocmset = ipw_tiocmset, 587282361a0SGreg Kroah-Hartman }; 588282361a0SGreg Kroah-Hartman 589282361a0SGreg Kroah-Hartman int ipwireless_tty_init(void) 590282361a0SGreg Kroah-Hartman { 591282361a0SGreg Kroah-Hartman int result; 592282361a0SGreg Kroah-Hartman 593282361a0SGreg Kroah-Hartman ipw_tty_driver = alloc_tty_driver(IPWIRELESS_PCMCIA_MINORS); 594282361a0SGreg Kroah-Hartman if (!ipw_tty_driver) 595282361a0SGreg Kroah-Hartman return -ENOMEM; 596282361a0SGreg Kroah-Hartman 597282361a0SGreg Kroah-Hartman ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME; 598282361a0SGreg Kroah-Hartman ipw_tty_driver->name = "ttyIPWp"; 599282361a0SGreg Kroah-Hartman ipw_tty_driver->major = 0; 600282361a0SGreg Kroah-Hartman ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START; 601282361a0SGreg Kroah-Hartman ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; 602282361a0SGreg Kroah-Hartman ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL; 603282361a0SGreg Kroah-Hartman ipw_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 604282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios = tty_std_termios; 605282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_cflag = 606282361a0SGreg Kroah-Hartman B9600 | CS8 | CREAD | HUPCL | CLOCAL; 607282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_ispeed = 9600; 608282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_ospeed = 9600; 609282361a0SGreg Kroah-Hartman tty_set_operations(ipw_tty_driver, &tty_ops); 610282361a0SGreg Kroah-Hartman result = tty_register_driver(ipw_tty_driver); 611282361a0SGreg Kroah-Hartman if (result) { 612282361a0SGreg Kroah-Hartman printk(KERN_ERR IPWIRELESS_PCCARD_NAME 613282361a0SGreg Kroah-Hartman ": failed to register tty driver\n"); 614282361a0SGreg Kroah-Hartman put_tty_driver(ipw_tty_driver); 615282361a0SGreg Kroah-Hartman return result; 616282361a0SGreg Kroah-Hartman } 617282361a0SGreg Kroah-Hartman 618282361a0SGreg Kroah-Hartman return 0; 619282361a0SGreg Kroah-Hartman } 620282361a0SGreg Kroah-Hartman 621282361a0SGreg Kroah-Hartman void ipwireless_tty_release(void) 622282361a0SGreg Kroah-Hartman { 623282361a0SGreg Kroah-Hartman int ret; 624282361a0SGreg Kroah-Hartman 625282361a0SGreg Kroah-Hartman ret = tty_unregister_driver(ipw_tty_driver); 626282361a0SGreg Kroah-Hartman put_tty_driver(ipw_tty_driver); 627282361a0SGreg Kroah-Hartman if (ret != 0) 628282361a0SGreg Kroah-Hartman printk(KERN_ERR IPWIRELESS_PCCARD_NAME 629282361a0SGreg Kroah-Hartman ": tty_unregister_driver failed with code %d\n", ret); 630282361a0SGreg Kroah-Hartman } 631282361a0SGreg Kroah-Hartman 632282361a0SGreg Kroah-Hartman int ipwireless_tty_is_modem(struct ipw_tty *tty) 633282361a0SGreg Kroah-Hartman { 634282361a0SGreg Kroah-Hartman return tty->tty_type == TTYTYPE_MODEM; 635282361a0SGreg Kroah-Hartman } 636282361a0SGreg Kroah-Hartman 637282361a0SGreg Kroah-Hartman void 638282361a0SGreg Kroah-Hartman ipwireless_tty_notify_control_line_change(struct ipw_tty *tty, 639282361a0SGreg Kroah-Hartman unsigned int channel_idx, 640282361a0SGreg Kroah-Hartman unsigned int control_lines, 641282361a0SGreg Kroah-Hartman unsigned int changed_mask) 642282361a0SGreg Kroah-Hartman { 643282361a0SGreg Kroah-Hartman unsigned int old_control_lines = tty->control_lines; 644282361a0SGreg Kroah-Hartman 645282361a0SGreg Kroah-Hartman tty->control_lines = (tty->control_lines & ~changed_mask) 646282361a0SGreg Kroah-Hartman | (control_lines & changed_mask); 647282361a0SGreg Kroah-Hartman 648282361a0SGreg Kroah-Hartman /* 649282361a0SGreg Kroah-Hartman * If DCD is de-asserted, we close the tty so pppd can tell that we 650282361a0SGreg Kroah-Hartman * have gone offline. 651282361a0SGreg Kroah-Hartman */ 652282361a0SGreg Kroah-Hartman if ((old_control_lines & IPW_CONTROL_LINE_DCD) 653282361a0SGreg Kroah-Hartman && !(tty->control_lines & IPW_CONTROL_LINE_DCD) 65419ef1b71SJiri Slaby && tty->port.tty) { 65519ef1b71SJiri Slaby tty_hangup(tty->port.tty); 656282361a0SGreg Kroah-Hartman } 657282361a0SGreg Kroah-Hartman } 658282361a0SGreg Kroah-Hartman 659