1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 2282361a0SGreg Kroah-Hartman /* 3282361a0SGreg Kroah-Hartman * IPWireless 3G PCMCIA Network Driver 4282361a0SGreg Kroah-Hartman * 5282361a0SGreg Kroah-Hartman * Original code 6282361a0SGreg Kroah-Hartman * by Stephen Blackheath <stephen@blacksapphire.com>, 7282361a0SGreg Kroah-Hartman * Ben Martel <benm@symmetric.co.nz> 8282361a0SGreg Kroah-Hartman * 9282361a0SGreg Kroah-Hartman * Copyrighted as follows: 10282361a0SGreg Kroah-Hartman * Copyright (C) 2004 by Symmetric Systems Ltd (NZ) 11282361a0SGreg Kroah-Hartman * 12282361a0SGreg Kroah-Hartman * Various driver changes and rewrites, port to new kernels 13282361a0SGreg Kroah-Hartman * Copyright (C) 2006-2007 Jiri Kosina 14282361a0SGreg Kroah-Hartman * 15282361a0SGreg Kroah-Hartman * Misc code cleanups and updates 16282361a0SGreg Kroah-Hartman * Copyright (C) 2007 David Sterba 17282361a0SGreg Kroah-Hartman */ 18282361a0SGreg Kroah-Hartman 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); 977393af80SJiri Slaby if (tty->port.count == 0) 98282361a0SGreg Kroah-Hartman tty->tx_bytes_queued = 0; 99282361a0SGreg Kroah-Hartman 1007393af80SJiri Slaby tty->port.count++; 101282361a0SGreg Kroah-Hartman 10219ef1b71SJiri Slaby tty->port.tty = linux_tty; 103282361a0SGreg Kroah-Hartman linux_tty->driver_data = tty; 104282361a0SGreg Kroah-Hartman 105282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM) 106282361a0SGreg Kroah-Hartman ipwireless_ppp_open(tty->network); 107282361a0SGreg Kroah-Hartman 108282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 109282361a0SGreg Kroah-Hartman 110282361a0SGreg Kroah-Hartman return 0; 111282361a0SGreg Kroah-Hartman } 112282361a0SGreg Kroah-Hartman 113282361a0SGreg Kroah-Hartman static void do_ipw_close(struct ipw_tty *tty) 114282361a0SGreg Kroah-Hartman { 1157393af80SJiri Slaby tty->port.count--; 116282361a0SGreg Kroah-Hartman 1177393af80SJiri Slaby if (tty->port.count == 0) { 11819ef1b71SJiri Slaby struct tty_struct *linux_tty = tty->port.tty; 119282361a0SGreg Kroah-Hartman 120282361a0SGreg Kroah-Hartman if (linux_tty != NULL) { 12119ef1b71SJiri Slaby tty->port.tty = NULL; 122282361a0SGreg Kroah-Hartman linux_tty->driver_data = NULL; 123282361a0SGreg Kroah-Hartman 124282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM) 125282361a0SGreg Kroah-Hartman ipwireless_ppp_close(tty->network); 126282361a0SGreg Kroah-Hartman } 127282361a0SGreg Kroah-Hartman } 128282361a0SGreg Kroah-Hartman } 129282361a0SGreg Kroah-Hartman 130282361a0SGreg Kroah-Hartman static void ipw_hangup(struct tty_struct *linux_tty) 131282361a0SGreg Kroah-Hartman { 132282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 133282361a0SGreg Kroah-Hartman 134282361a0SGreg Kroah-Hartman if (!tty) 135282361a0SGreg Kroah-Hartman return; 136282361a0SGreg Kroah-Hartman 137282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex); 1387393af80SJiri Slaby if (tty->port.count == 0) { 139282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 140282361a0SGreg Kroah-Hartman return; 141282361a0SGreg Kroah-Hartman } 142282361a0SGreg Kroah-Hartman 143282361a0SGreg Kroah-Hartman do_ipw_close(tty); 144282361a0SGreg Kroah-Hartman 145282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 146282361a0SGreg Kroah-Hartman } 147282361a0SGreg Kroah-Hartman 148282361a0SGreg Kroah-Hartman static void ipw_close(struct tty_struct *linux_tty, struct file *filp) 149282361a0SGreg Kroah-Hartman { 150282361a0SGreg Kroah-Hartman ipw_hangup(linux_tty); 151282361a0SGreg Kroah-Hartman } 152282361a0SGreg Kroah-Hartman 153282361a0SGreg Kroah-Hartman /* Take data received from hardware, and send it out the tty */ 154282361a0SGreg Kroah-Hartman void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, 155282361a0SGreg Kroah-Hartman unsigned int length) 156282361a0SGreg Kroah-Hartman { 157282361a0SGreg Kroah-Hartman int work = 0; 158282361a0SGreg Kroah-Hartman 159282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex); 160282361a0SGreg Kroah-Hartman 1617393af80SJiri Slaby if (!tty->port.count) { 162282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 163282361a0SGreg Kroah-Hartman return; 164282361a0SGreg Kroah-Hartman } 165282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 166282361a0SGreg Kroah-Hartman 16705c7cd39SJiri Slaby work = tty_insert_flip_string(&tty->port, data, length); 168282361a0SGreg Kroah-Hartman 169282361a0SGreg Kroah-Hartman if (work != length) 170282361a0SGreg Kroah-Hartman printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME 171282361a0SGreg Kroah-Hartman ": %d chars not inserted to flip buffer!\n", 172282361a0SGreg Kroah-Hartman length - work); 173282361a0SGreg Kroah-Hartman 174282361a0SGreg Kroah-Hartman if (work) 1752e124b4aSJiri Slaby tty_flip_buffer_push(&tty->port); 176282361a0SGreg Kroah-Hartman } 177282361a0SGreg Kroah-Hartman 178282361a0SGreg Kroah-Hartman static void ipw_write_packet_sent_callback(void *callback_data, 179282361a0SGreg Kroah-Hartman unsigned int packet_length) 180282361a0SGreg Kroah-Hartman { 181282361a0SGreg Kroah-Hartman struct ipw_tty *tty = callback_data; 182282361a0SGreg Kroah-Hartman 183282361a0SGreg Kroah-Hartman /* 184282361a0SGreg Kroah-Hartman * Packet has been sent, so we subtract the number of bytes from our 185282361a0SGreg Kroah-Hartman * tally of outstanding TX bytes. 186282361a0SGreg Kroah-Hartman */ 187282361a0SGreg Kroah-Hartman tty->tx_bytes_queued -= packet_length; 188282361a0SGreg Kroah-Hartman } 189282361a0SGreg Kroah-Hartman 190282361a0SGreg Kroah-Hartman static int ipw_write(struct tty_struct *linux_tty, 191282361a0SGreg Kroah-Hartman const unsigned char *buf, int count) 192282361a0SGreg Kroah-Hartman { 193282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 194282361a0SGreg Kroah-Hartman int room, ret; 195282361a0SGreg Kroah-Hartman 196282361a0SGreg Kroah-Hartman if (!tty) 197282361a0SGreg Kroah-Hartman return -ENODEV; 198282361a0SGreg Kroah-Hartman 199282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex); 2007393af80SJiri Slaby if (!tty->port.count) { 201282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 202282361a0SGreg Kroah-Hartman return -EINVAL; 203282361a0SGreg Kroah-Hartman } 204282361a0SGreg Kroah-Hartman 205282361a0SGreg Kroah-Hartman room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; 206282361a0SGreg Kroah-Hartman if (room < 0) 207282361a0SGreg Kroah-Hartman room = 0; 208282361a0SGreg Kroah-Hartman /* Don't allow caller to write any more than we have room for */ 209282361a0SGreg Kroah-Hartman if (count > room) 210282361a0SGreg Kroah-Hartman count = room; 211282361a0SGreg Kroah-Hartman 212282361a0SGreg Kroah-Hartman if (count == 0) { 213282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 214282361a0SGreg Kroah-Hartman return 0; 215282361a0SGreg Kroah-Hartman } 216282361a0SGreg Kroah-Hartman 217282361a0SGreg Kroah-Hartman ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS, 218282361a0SGreg Kroah-Hartman buf, count, 219282361a0SGreg Kroah-Hartman ipw_write_packet_sent_callback, tty); 220db332356STong Zhang if (ret < 0) { 221282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 222282361a0SGreg Kroah-Hartman return 0; 223282361a0SGreg Kroah-Hartman } 224282361a0SGreg Kroah-Hartman 225282361a0SGreg Kroah-Hartman tty->tx_bytes_queued += count; 226282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex); 227282361a0SGreg Kroah-Hartman 228282361a0SGreg Kroah-Hartman return count; 229282361a0SGreg Kroah-Hartman } 230282361a0SGreg Kroah-Hartman 231*03b3b1a2SJiri Slaby static unsigned int ipw_write_room(struct tty_struct *linux_tty) 232282361a0SGreg Kroah-Hartman { 233282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 234282361a0SGreg Kroah-Hartman int room; 235282361a0SGreg Kroah-Hartman 236282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 237282361a0SGreg Kroah-Hartman if (!tty) 2386bfbfcfcSJiri Slaby return 0; 239282361a0SGreg Kroah-Hartman 2407393af80SJiri Slaby if (!tty->port.count) 2416bfbfcfcSJiri Slaby return 0; 242282361a0SGreg Kroah-Hartman 243282361a0SGreg Kroah-Hartman room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; 244282361a0SGreg Kroah-Hartman if (room < 0) 245282361a0SGreg Kroah-Hartman room = 0; 246282361a0SGreg Kroah-Hartman 247282361a0SGreg Kroah-Hartman return room; 248282361a0SGreg Kroah-Hartman } 249282361a0SGreg Kroah-Hartman 250a7b06fcfSAl Viro static int ipwireless_get_serial_info(struct tty_struct *linux_tty, 251a7b06fcfSAl Viro struct serial_struct *ss) 252282361a0SGreg Kroah-Hartman { 253a7b06fcfSAl Viro struct ipw_tty *tty = linux_tty->driver_data; 254282361a0SGreg Kroah-Hartman 255a7b06fcfSAl Viro if (!tty) 256a7b06fcfSAl Viro return -ENODEV; 257fec4daecSJiri Slaby 258a7b06fcfSAl Viro if (!tty->port.count) 259a7b06fcfSAl Viro return -EINVAL; 260282361a0SGreg Kroah-Hartman 261a7b06fcfSAl Viro ss->type = PORT_UNKNOWN; 262a7b06fcfSAl Viro ss->line = tty->index; 263a7b06fcfSAl Viro ss->baud_base = 115200; 264282361a0SGreg Kroah-Hartman return 0; 265282361a0SGreg Kroah-Hartman } 266282361a0SGreg Kroah-Hartman 267a7b06fcfSAl Viro static int ipwireless_set_serial_info(struct tty_struct *linux_tty, 268a7b06fcfSAl Viro struct serial_struct *ss) 269a7b06fcfSAl Viro { 270a7b06fcfSAl Viro return 0; /* Keeps the PCMCIA scripts happy. */ 271a7b06fcfSAl Viro } 272a7b06fcfSAl Viro 273282361a0SGreg Kroah-Hartman static int ipw_chars_in_buffer(struct tty_struct *linux_tty) 274282361a0SGreg Kroah-Hartman { 275282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 276282361a0SGreg Kroah-Hartman 277282361a0SGreg Kroah-Hartman if (!tty) 278282361a0SGreg Kroah-Hartman return 0; 279282361a0SGreg Kroah-Hartman 2807393af80SJiri Slaby if (!tty->port.count) 281282361a0SGreg Kroah-Hartman return 0; 282282361a0SGreg Kroah-Hartman 283282361a0SGreg Kroah-Hartman return tty->tx_bytes_queued; 284282361a0SGreg Kroah-Hartman } 285282361a0SGreg Kroah-Hartman 286282361a0SGreg Kroah-Hartman static int get_control_lines(struct ipw_tty *tty) 287282361a0SGreg Kroah-Hartman { 288282361a0SGreg Kroah-Hartman unsigned int my = tty->control_lines; 289282361a0SGreg Kroah-Hartman unsigned int out = 0; 290282361a0SGreg Kroah-Hartman 291282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_RTS) 292282361a0SGreg Kroah-Hartman out |= TIOCM_RTS; 293282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DTR) 294282361a0SGreg Kroah-Hartman out |= TIOCM_DTR; 295282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_CTS) 296282361a0SGreg Kroah-Hartman out |= TIOCM_CTS; 297282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DSR) 298282361a0SGreg Kroah-Hartman out |= TIOCM_DSR; 299282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DCD) 300282361a0SGreg Kroah-Hartman out |= TIOCM_CD; 301282361a0SGreg Kroah-Hartman 302282361a0SGreg Kroah-Hartman return out; 303282361a0SGreg Kroah-Hartman } 304282361a0SGreg Kroah-Hartman 305282361a0SGreg Kroah-Hartman static int set_control_lines(struct ipw_tty *tty, unsigned int set, 306282361a0SGreg Kroah-Hartman unsigned int clear) 307282361a0SGreg Kroah-Hartman { 308282361a0SGreg Kroah-Hartman int ret; 309282361a0SGreg Kroah-Hartman 310282361a0SGreg Kroah-Hartman if (set & TIOCM_RTS) { 311282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1); 312282361a0SGreg Kroah-Hartman if (ret) 313282361a0SGreg Kroah-Hartman return ret; 314282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 315282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, 316282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 1); 317282361a0SGreg Kroah-Hartman if (ret) 318282361a0SGreg Kroah-Hartman return ret; 319282361a0SGreg Kroah-Hartman } 320282361a0SGreg Kroah-Hartman } 321282361a0SGreg Kroah-Hartman if (set & TIOCM_DTR) { 322282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1); 323282361a0SGreg Kroah-Hartman if (ret) 324282361a0SGreg Kroah-Hartman return ret; 325282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 326282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, 327282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 1); 328282361a0SGreg Kroah-Hartman if (ret) 329282361a0SGreg Kroah-Hartman return ret; 330282361a0SGreg Kroah-Hartman } 331282361a0SGreg Kroah-Hartman } 332282361a0SGreg Kroah-Hartman if (clear & TIOCM_RTS) { 333282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0); 334282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 335282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, 336282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 0); 337282361a0SGreg Kroah-Hartman if (ret) 338282361a0SGreg Kroah-Hartman return ret; 339282361a0SGreg Kroah-Hartman } 340282361a0SGreg Kroah-Hartman } 341282361a0SGreg Kroah-Hartman if (clear & TIOCM_DTR) { 342282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0); 343282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) { 344282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, 345282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 0); 346282361a0SGreg Kroah-Hartman if (ret) 347282361a0SGreg Kroah-Hartman return ret; 348282361a0SGreg Kroah-Hartman } 349282361a0SGreg Kroah-Hartman } 350282361a0SGreg Kroah-Hartman return 0; 351282361a0SGreg Kroah-Hartman } 352282361a0SGreg Kroah-Hartman 353282361a0SGreg Kroah-Hartman static int ipw_tiocmget(struct tty_struct *linux_tty) 354282361a0SGreg Kroah-Hartman { 355282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 356282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 357282361a0SGreg Kroah-Hartman 358282361a0SGreg Kroah-Hartman if (!tty) 359282361a0SGreg Kroah-Hartman return -ENODEV; 360282361a0SGreg Kroah-Hartman 3617393af80SJiri Slaby if (!tty->port.count) 362282361a0SGreg Kroah-Hartman return -EINVAL; 363282361a0SGreg Kroah-Hartman 364282361a0SGreg Kroah-Hartman return get_control_lines(tty); 365282361a0SGreg Kroah-Hartman } 366282361a0SGreg Kroah-Hartman 367282361a0SGreg Kroah-Hartman static int 368282361a0SGreg Kroah-Hartman ipw_tiocmset(struct tty_struct *linux_tty, 369282361a0SGreg Kroah-Hartman unsigned int set, unsigned int clear) 370282361a0SGreg Kroah-Hartman { 371282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 372282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 373282361a0SGreg Kroah-Hartman 374282361a0SGreg Kroah-Hartman if (!tty) 375282361a0SGreg Kroah-Hartman return -ENODEV; 376282361a0SGreg Kroah-Hartman 3777393af80SJiri Slaby if (!tty->port.count) 378282361a0SGreg Kroah-Hartman return -EINVAL; 379282361a0SGreg Kroah-Hartman 380282361a0SGreg Kroah-Hartman return set_control_lines(tty, set, clear); 381282361a0SGreg Kroah-Hartman } 382282361a0SGreg Kroah-Hartman 383282361a0SGreg Kroah-Hartman static int ipw_ioctl(struct tty_struct *linux_tty, 384282361a0SGreg Kroah-Hartman unsigned int cmd, unsigned long arg) 385282361a0SGreg Kroah-Hartman { 386282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data; 387282361a0SGreg Kroah-Hartman 388282361a0SGreg Kroah-Hartman if (!tty) 389282361a0SGreg Kroah-Hartman return -ENODEV; 390282361a0SGreg Kroah-Hartman 3917393af80SJiri Slaby if (!tty->port.count) 392282361a0SGreg Kroah-Hartman return -EINVAL; 393282361a0SGreg Kroah-Hartman 394282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */ 395282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM) { 396282361a0SGreg Kroah-Hartman switch (cmd) { 397282361a0SGreg Kroah-Hartman case PPPIOCGCHAN: 398282361a0SGreg Kroah-Hartman { 399282361a0SGreg Kroah-Hartman int chan = ipwireless_ppp_channel_index( 400282361a0SGreg Kroah-Hartman tty->network); 401282361a0SGreg Kroah-Hartman 402282361a0SGreg Kroah-Hartman if (chan < 0) 403282361a0SGreg Kroah-Hartman return -ENODEV; 404282361a0SGreg Kroah-Hartman if (put_user(chan, (int __user *) arg)) 405282361a0SGreg Kroah-Hartman return -EFAULT; 406282361a0SGreg Kroah-Hartman } 407282361a0SGreg Kroah-Hartman return 0; 408282361a0SGreg Kroah-Hartman 409282361a0SGreg Kroah-Hartman case PPPIOCGUNIT: 410282361a0SGreg Kroah-Hartman { 411282361a0SGreg Kroah-Hartman int unit = ipwireless_ppp_unit_number( 412282361a0SGreg Kroah-Hartman tty->network); 413282361a0SGreg Kroah-Hartman 414282361a0SGreg Kroah-Hartman if (unit < 0) 415282361a0SGreg Kroah-Hartman return -ENODEV; 416282361a0SGreg Kroah-Hartman if (put_user(unit, (int __user *) arg)) 417282361a0SGreg Kroah-Hartman return -EFAULT; 418282361a0SGreg Kroah-Hartman } 419282361a0SGreg Kroah-Hartman return 0; 420282361a0SGreg Kroah-Hartman 421282361a0SGreg Kroah-Hartman case FIONREAD: 422282361a0SGreg Kroah-Hartman { 423282361a0SGreg Kroah-Hartman int val = 0; 424282361a0SGreg Kroah-Hartman 425282361a0SGreg Kroah-Hartman if (put_user(val, (int __user *) arg)) 426282361a0SGreg Kroah-Hartman return -EFAULT; 427282361a0SGreg Kroah-Hartman } 428282361a0SGreg Kroah-Hartman return 0; 429282361a0SGreg Kroah-Hartman case TCFLSH: 430282361a0SGreg Kroah-Hartman return tty_perform_flush(linux_tty, arg); 431282361a0SGreg Kroah-Hartman } 432282361a0SGreg Kroah-Hartman } 433282361a0SGreg Kroah-Hartman return -ENOIOCTLCMD; 434282361a0SGreg Kroah-Hartman } 435282361a0SGreg Kroah-Hartman 436282361a0SGreg Kroah-Hartman static int add_tty(int j, 437282361a0SGreg Kroah-Hartman struct ipw_hardware *hardware, 438282361a0SGreg Kroah-Hartman struct ipw_network *network, int channel_idx, 439282361a0SGreg Kroah-Hartman int secondary_channel_idx, int tty_type) 440282361a0SGreg Kroah-Hartman { 441282361a0SGreg Kroah-Hartman ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL); 442282361a0SGreg Kroah-Hartman if (!ttys[j]) 443282361a0SGreg Kroah-Hartman return -ENOMEM; 444282361a0SGreg Kroah-Hartman ttys[j]->index = j; 445282361a0SGreg Kroah-Hartman ttys[j]->hardware = hardware; 446282361a0SGreg Kroah-Hartman ttys[j]->channel_idx = channel_idx; 447282361a0SGreg Kroah-Hartman ttys[j]->secondary_channel_idx = secondary_channel_idx; 448282361a0SGreg Kroah-Hartman ttys[j]->network = network; 449282361a0SGreg Kroah-Hartman ttys[j]->tty_type = tty_type; 450282361a0SGreg Kroah-Hartman mutex_init(&ttys[j]->ipw_tty_mutex); 4517393af80SJiri Slaby tty_port_init(&ttys[j]->port); 452282361a0SGreg Kroah-Hartman 453734cc178SJiri Slaby tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL); 454282361a0SGreg Kroah-Hartman ipwireless_associate_network_tty(network, channel_idx, ttys[j]); 455282361a0SGreg Kroah-Hartman 456282361a0SGreg Kroah-Hartman if (secondary_channel_idx != -1) 457282361a0SGreg Kroah-Hartman ipwireless_associate_network_tty(network, 458282361a0SGreg Kroah-Hartman secondary_channel_idx, 459282361a0SGreg Kroah-Hartman ttys[j]); 460e6df3cceSJiri Slaby /* check if we provide raw device (if loopback is enabled) */ 461e6df3cceSJiri Slaby if (get_tty(j)) 462e6df3cceSJiri Slaby printk(KERN_INFO IPWIRELESS_PCCARD_NAME 463e6df3cceSJiri Slaby ": registering %s device ttyIPWp%d\n", 464e6df3cceSJiri Slaby tty_type_name(tty_type), j); 465e6df3cceSJiri Slaby 466282361a0SGreg Kroah-Hartman return 0; 467282361a0SGreg Kroah-Hartman } 468282361a0SGreg Kroah-Hartman 469282361a0SGreg Kroah-Hartman struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, 470282361a0SGreg Kroah-Hartman struct ipw_network *network) 471282361a0SGreg Kroah-Hartman { 472282361a0SGreg Kroah-Hartman int i, j; 473282361a0SGreg Kroah-Hartman 474282361a0SGreg Kroah-Hartman for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) { 475282361a0SGreg Kroah-Hartman int allfree = 1; 476282361a0SGreg Kroah-Hartman 477282361a0SGreg Kroah-Hartman for (j = i; j < IPWIRELESS_PCMCIA_MINORS; 478282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE) 479282361a0SGreg Kroah-Hartman if (ttys[j] != NULL) { 480282361a0SGreg Kroah-Hartman allfree = 0; 481282361a0SGreg Kroah-Hartman break; 482282361a0SGreg Kroah-Hartman } 483282361a0SGreg Kroah-Hartman 484282361a0SGreg Kroah-Hartman if (allfree) { 485282361a0SGreg Kroah-Hartman j = i; 486282361a0SGreg Kroah-Hartman 487282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network, 488282361a0SGreg Kroah-Hartman IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS, 489282361a0SGreg Kroah-Hartman TTYTYPE_MODEM)) 490282361a0SGreg Kroah-Hartman return NULL; 491282361a0SGreg Kroah-Hartman 492282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE; 493282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network, 494282361a0SGreg Kroah-Hartman IPW_CHANNEL_DIALLER, -1, 495282361a0SGreg Kroah-Hartman TTYTYPE_MONITOR)) 496282361a0SGreg Kroah-Hartman return NULL; 497282361a0SGreg Kroah-Hartman 498282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE; 499282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network, 500282361a0SGreg Kroah-Hartman IPW_CHANNEL_RAS, -1, 501282361a0SGreg Kroah-Hartman TTYTYPE_RAS_RAW)) 502282361a0SGreg Kroah-Hartman return NULL; 503282361a0SGreg Kroah-Hartman 504282361a0SGreg Kroah-Hartman return ttys[i]; 505282361a0SGreg Kroah-Hartman } 506282361a0SGreg Kroah-Hartman } 507282361a0SGreg Kroah-Hartman return NULL; 508282361a0SGreg Kroah-Hartman } 509282361a0SGreg Kroah-Hartman 510282361a0SGreg Kroah-Hartman /* 511282361a0SGreg Kroah-Hartman * Must be called before ipwireless_network_free(). 512282361a0SGreg Kroah-Hartman */ 513282361a0SGreg Kroah-Hartman void ipwireless_tty_free(struct ipw_tty *tty) 514282361a0SGreg Kroah-Hartman { 515282361a0SGreg Kroah-Hartman int j; 516282361a0SGreg Kroah-Hartman struct ipw_network *network = ttys[tty->index]->network; 517282361a0SGreg Kroah-Hartman 518282361a0SGreg Kroah-Hartman for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS; 519282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE) { 520282361a0SGreg Kroah-Hartman struct ipw_tty *ttyj = ttys[j]; 521282361a0SGreg Kroah-Hartman 522282361a0SGreg Kroah-Hartman if (ttyj) { 523282361a0SGreg Kroah-Hartman mutex_lock(&ttyj->ipw_tty_mutex); 524e6df3cceSJiri Slaby if (get_tty(j)) 525e6df3cceSJiri Slaby printk(KERN_INFO IPWIRELESS_PCCARD_NAME 526e6df3cceSJiri Slaby ": deregistering %s device ttyIPWp%d\n", 527e6df3cceSJiri Slaby tty_type_name(ttyj->tty_type), j); 528282361a0SGreg Kroah-Hartman ttyj->closing = 1; 52919ef1b71SJiri Slaby if (ttyj->port.tty != NULL) { 530282361a0SGreg Kroah-Hartman mutex_unlock(&ttyj->ipw_tty_mutex); 53119ef1b71SJiri Slaby tty_vhangup(ttyj->port.tty); 532282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here 533282361a0SGreg Kroah-Hartman against a parallel ioctl etc */ 534de3a60a3SJiri Slaby /* FIXME2: hangup does not mean all processes 535de3a60a3SJiri Slaby * are gone */ 536282361a0SGreg Kroah-Hartman mutex_lock(&ttyj->ipw_tty_mutex); 537282361a0SGreg Kroah-Hartman } 5387393af80SJiri Slaby while (ttyj->port.count) 539282361a0SGreg Kroah-Hartman do_ipw_close(ttyj); 540282361a0SGreg Kroah-Hartman ipwireless_disassociate_network_ttys(network, 541282361a0SGreg Kroah-Hartman ttyj->channel_idx); 542282361a0SGreg Kroah-Hartman tty_unregister_device(ipw_tty_driver, j); 543191c5f10SJiri Slaby tty_port_destroy(&ttyj->port); 544282361a0SGreg Kroah-Hartman ttys[j] = NULL; 545282361a0SGreg Kroah-Hartman mutex_unlock(&ttyj->ipw_tty_mutex); 546282361a0SGreg Kroah-Hartman kfree(ttyj); 547282361a0SGreg Kroah-Hartman } 548282361a0SGreg Kroah-Hartman } 549282361a0SGreg Kroah-Hartman } 550282361a0SGreg Kroah-Hartman 551282361a0SGreg Kroah-Hartman static const struct tty_operations tty_ops = { 552282361a0SGreg Kroah-Hartman .open = ipw_open, 553282361a0SGreg Kroah-Hartman .close = ipw_close, 554282361a0SGreg Kroah-Hartman .hangup = ipw_hangup, 555282361a0SGreg Kroah-Hartman .write = ipw_write, 556282361a0SGreg Kroah-Hartman .write_room = ipw_write_room, 557282361a0SGreg Kroah-Hartman .ioctl = ipw_ioctl, 558282361a0SGreg Kroah-Hartman .chars_in_buffer = ipw_chars_in_buffer, 559282361a0SGreg Kroah-Hartman .tiocmget = ipw_tiocmget, 560282361a0SGreg Kroah-Hartman .tiocmset = ipw_tiocmset, 561a7b06fcfSAl Viro .set_serial = ipwireless_set_serial_info, 562a7b06fcfSAl Viro .get_serial = ipwireless_get_serial_info, 563282361a0SGreg Kroah-Hartman }; 564282361a0SGreg Kroah-Hartman 565282361a0SGreg Kroah-Hartman int ipwireless_tty_init(void) 566282361a0SGreg Kroah-Hartman { 567282361a0SGreg Kroah-Hartman int result; 568282361a0SGreg Kroah-Hartman 569282361a0SGreg Kroah-Hartman ipw_tty_driver = alloc_tty_driver(IPWIRELESS_PCMCIA_MINORS); 570282361a0SGreg Kroah-Hartman if (!ipw_tty_driver) 571282361a0SGreg Kroah-Hartman return -ENOMEM; 572282361a0SGreg Kroah-Hartman 573282361a0SGreg Kroah-Hartman ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME; 574282361a0SGreg Kroah-Hartman ipw_tty_driver->name = "ttyIPWp"; 575282361a0SGreg Kroah-Hartman ipw_tty_driver->major = 0; 576282361a0SGreg Kroah-Hartman ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START; 577282361a0SGreg Kroah-Hartman ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; 578282361a0SGreg Kroah-Hartman ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL; 579282361a0SGreg Kroah-Hartman ipw_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; 580282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios = tty_std_termios; 581282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_cflag = 582282361a0SGreg Kroah-Hartman B9600 | CS8 | CREAD | HUPCL | CLOCAL; 583282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_ispeed = 9600; 584282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_ospeed = 9600; 585282361a0SGreg Kroah-Hartman tty_set_operations(ipw_tty_driver, &tty_ops); 586282361a0SGreg Kroah-Hartman result = tty_register_driver(ipw_tty_driver); 587282361a0SGreg Kroah-Hartman if (result) { 588282361a0SGreg Kroah-Hartman printk(KERN_ERR IPWIRELESS_PCCARD_NAME 589282361a0SGreg Kroah-Hartman ": failed to register tty driver\n"); 590282361a0SGreg Kroah-Hartman put_tty_driver(ipw_tty_driver); 591282361a0SGreg Kroah-Hartman return result; 592282361a0SGreg Kroah-Hartman } 593282361a0SGreg Kroah-Hartman 594282361a0SGreg Kroah-Hartman return 0; 595282361a0SGreg Kroah-Hartman } 596282361a0SGreg Kroah-Hartman 597282361a0SGreg Kroah-Hartman void ipwireless_tty_release(void) 598282361a0SGreg Kroah-Hartman { 5996c2e6317SJiri Slaby tty_unregister_driver(ipw_tty_driver); 600282361a0SGreg Kroah-Hartman put_tty_driver(ipw_tty_driver); 601282361a0SGreg Kroah-Hartman } 602282361a0SGreg Kroah-Hartman 603282361a0SGreg Kroah-Hartman int ipwireless_tty_is_modem(struct ipw_tty *tty) 604282361a0SGreg Kroah-Hartman { 605282361a0SGreg Kroah-Hartman return tty->tty_type == TTYTYPE_MODEM; 606282361a0SGreg Kroah-Hartman } 607282361a0SGreg Kroah-Hartman 608282361a0SGreg Kroah-Hartman void 609282361a0SGreg Kroah-Hartman ipwireless_tty_notify_control_line_change(struct ipw_tty *tty, 610282361a0SGreg Kroah-Hartman unsigned int channel_idx, 611282361a0SGreg Kroah-Hartman unsigned int control_lines, 612282361a0SGreg Kroah-Hartman unsigned int changed_mask) 613282361a0SGreg Kroah-Hartman { 614282361a0SGreg Kroah-Hartman unsigned int old_control_lines = tty->control_lines; 615282361a0SGreg Kroah-Hartman 616282361a0SGreg Kroah-Hartman tty->control_lines = (tty->control_lines & ~changed_mask) 617282361a0SGreg Kroah-Hartman | (control_lines & changed_mask); 618282361a0SGreg Kroah-Hartman 619282361a0SGreg Kroah-Hartman /* 620282361a0SGreg Kroah-Hartman * If DCD is de-asserted, we close the tty so pppd can tell that we 621282361a0SGreg Kroah-Hartman * have gone offline. 622282361a0SGreg Kroah-Hartman */ 623282361a0SGreg Kroah-Hartman if ((old_control_lines & IPW_CONTROL_LINE_DCD) 624282361a0SGreg Kroah-Hartman && !(tty->control_lines & IPW_CONTROL_LINE_DCD) 62519ef1b71SJiri Slaby && tty->port.tty) { 62619ef1b71SJiri Slaby tty_hangup(tty->port.tty); 627282361a0SGreg Kroah-Hartman } 628282361a0SGreg Kroah-Hartman } 629282361a0SGreg Kroah-Hartman 630