cregit-Linux how code gets into the kernel

Release 4.7 drivers/tty/ipwireless/tty.c

/*
 * IPWireless 3G PCMCIA Network Driver
 *
 * Original code
 *   by Stephen Blackheath <stephen@blacksapphire.com>,
 *      Ben Martel <benm@symmetric.co.nz>
 *
 * Copyrighted as follows:
 *   Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
 *
 * Various driver changes and rewrites, port to new kernels
 *   Copyright (C) 2006-2007 Jiri Kosina
 *
 * Misc code cleanups and updates
 *   Copyright (C) 2007 David Sterba
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/ppp_defs.h>
#include <linux/if.h>
#include <linux/ppp-ioctl.h>
#include <linux/sched.h>
#include <linux/serial.h>
#include <linux/slab.h>
#include <linux/tty.h>
#include <linux/tty_driver.h>
#include <linux/tty_flip.h>
#include <linux/uaccess.h>

#include "tty.h"
#include "network.h"
#include "hardware.h"
#include "main.h"


#define IPWIRELESS_PCMCIA_START 	(0)

#define IPWIRELESS_PCMCIA_MINORS	(24)

#define IPWIRELESS_PCMCIA_MINOR_RANGE	(8)


#define TTYTYPE_MODEM    (0)

#define TTYTYPE_MONITOR  (1)

#define TTYTYPE_RAS_RAW  (2)


struct ipw_tty {
	
struct tty_port port;
	
int index;
	
struct ipw_hardware *hardware;
	
unsigned int channel_idx;
	
unsigned int secondary_channel_idx;
	
int tty_type;
	
struct ipw_network *network;
	
unsigned int control_lines;
	
struct mutex ipw_tty_mutex;
	
int tx_bytes_queued;
	
int closing;
};


static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS];


static struct tty_driver *ipw_tty_driver;


static char *tty_type_name(int tty_type) { static char *channel_names[] = { "modem", "monitor", "RAS-raw" }; return channel_names[tty_type]; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba30100.00%1100.00%
Total30100.00%1100.00%


static struct ipw_tty *get_tty(int index) { /* * The 'ras_raw' channel is only available when 'loopback' mode * is enabled. * Number of minor starts with 16 (_RANGE * _RAS_RAW). */ if (!ipwireless_loopback && index >= IPWIRELESS_PCMCIA_MINOR_RANGE * TTYTYPE_RAS_RAW) return NULL; return ttys[index]; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba2887.50%150.00%
jiri slabyjiri slaby412.50%150.00%
Total32100.00%2100.00%


static int ipw_open(struct tty_struct *linux_tty, struct file *filp) { struct ipw_tty *tty = get_tty(linux_tty->index); if (!tty) return -ENODEV; mutex_lock(&tty->ipw_tty_mutex); if (tty->port.count == 0) tty->tx_bytes_queued = 0; tty->port.count++; tty->port.tty = linux_tty; linux_tty->driver_data = tty; tty->port.low_latency = 1; if (tty->tty_type == TTYTYPE_MODEM) ipwireless_ppp_open(tty->network); mutex_unlock(&tty->ipw_tty_mutex); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba10187.07%120.00%
jiri slabyjiri slaby1512.93%480.00%
Total116100.00%5100.00%


static void do_ipw_close(struct ipw_tty *tty) { tty->port.count--; if (tty->port.count == 0) { struct tty_struct *linux_tty = tty->port.tty; if (linux_tty != NULL) { tty->port.tty = NULL; linux_tty->driver_data = NULL; if (tty->tty_type == TTYTYPE_MODEM) ipwireless_ppp_close(tty->network); } } }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba6684.62%133.33%
jiri slabyjiri slaby1215.38%266.67%
Total78100.00%3100.00%


static void ipw_hangup(struct tty_struct *linux_tty) { struct ipw_tty *tty = linux_tty->driver_data; if (!tty) return; mutex_lock(&tty->ipw_tty_mutex); if (tty->port.count == 0) { mutex_unlock(&tty->ipw_tty_mutex); return; } do_ipw_close(tty); mutex_unlock(&tty->ipw_tty_mutex); }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba6595.59%150.00%
jiri slabyjiri slaby34.41%150.00%
Total68100.00%2100.00%


static void ipw_close(struct tty_struct *linux_tty, struct file *filp) { ipw_hangup(linux_tty); }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba21100.00%1100.00%
Total21100.00%1100.00%

/* Take data received from hardware, and send it out the tty */
void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data, unsigned int length) { int work = 0; mutex_lock(&tty->ipw_tty_mutex); if (!tty->port.count) { mutex_unlock(&tty->ipw_tty_mutex); return; } mutex_unlock(&tty->ipw_tty_mutex); work = tty_insert_flip_string(&tty->port, data, length); if (work != length) printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": %d chars not inserted to flip buffer!\n", length - work); if (work) tty_flip_buffer_push(&tty->port); }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba8989.00%125.00%
jiri slabyjiri slaby1111.00%375.00%
Total100100.00%4100.00%


static void ipw_write_packet_sent_callback(void *callback_data, unsigned int packet_length) { struct ipw_tty *tty = callback_data; /* * Packet has been sent, so we subtract the number of bytes from our * tally of outstanding TX bytes. */ tty->tx_bytes_queued -= packet_length; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba28100.00%1100.00%
Total28100.00%1100.00%


static int ipw_write(struct tty_struct *linux_tty, const unsigned char *buf, int count) { struct ipw_tty *tty = linux_tty->driver_data; int room, ret; if (!tty) return -ENODEV; mutex_lock(&tty->ipw_tty_mutex); if (!tty->port.count) { mutex_unlock(&tty->ipw_tty_mutex); return -EINVAL; } room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; if (room < 0) room = 0; /* Don't allow caller to write any more than we have room for */ if (count > room) count = room; if (count == 0) { mutex_unlock(&tty->ipw_tty_mutex); return 0; } ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS, buf, count, ipw_write_packet_sent_callback, tty); if (ret == -1) { mutex_unlock(&tty->ipw_tty_mutex); return 0; } tty->tx_bytes_queued += count; mutex_unlock(&tty->ipw_tty_mutex); return count; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba17598.31%150.00%
jiri slabyjiri slaby31.69%150.00%
Total178100.00%2100.00%


static int ipw_write_room(struct tty_struct *linux_tty) { struct ipw_tty *tty = linux_tty->driver_data; int room; /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; if (!tty->port.count) return -EINVAL; room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued; if (room < 0) room = 0; return room; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba6394.03%133.33%
jiri slabyjiri slaby34.48%133.33%
alan coxalan cox11.49%133.33%
Total67100.00%3100.00%


static int ipwireless_get_serial_info(struct ipw_tty *tty, struct serial_struct __user *retinfo) { struct serial_struct tmp; if (!retinfo) return (-EFAULT); memset(&tmp, 0, sizeof(tmp)); tmp.type = PORT_UNKNOWN; tmp.line = tty->index; tmp.port = 0; tmp.irq = 0; tmp.flags = 0; tmp.baud_base = 115200; tmp.close_delay = 0; tmp.closing_wait = 0; tmp.custom_divisor = 0; tmp.hub6 = 0; if (copy_to_user(retinfo, &tmp, sizeof(*retinfo))) return -EFAULT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba130100.00%1100.00%
Total130100.00%1100.00%


static int ipw_chars_in_buffer(struct tty_struct *linux_tty) { struct ipw_tty *tty = linux_tty->driver_data; if (!tty) return 0; if (!tty->port.count) return 0; return tty->tx_bytes_queued; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba4088.89%133.33%
jiri slabyjiri slaby36.67%133.33%
alan coxalan cox24.44%133.33%
Total45100.00%3100.00%


static int get_control_lines(struct ipw_tty *tty) { unsigned int my = tty->control_lines; unsigned int out = 0; if (my & IPW_CONTROL_LINE_RTS) out |= TIOCM_RTS; if (my & IPW_CONTROL_LINE_DTR) out |= TIOCM_DTR; if (my & IPW_CONTROL_LINE_CTS) out |= TIOCM_CTS; if (my & IPW_CONTROL_LINE_DSR) out |= TIOCM_DSR; if (my & IPW_CONTROL_LINE_DCD) out |= TIOCM_CD; return out; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba78100.00%1100.00%
Total78100.00%1100.00%


static int set_control_lines(struct ipw_tty *tty, unsigned int set, unsigned int clear) { int ret; if (set & TIOCM_RTS) { ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1); if (ret) return ret; if (tty->secondary_channel_idx != -1) { ret = ipwireless_set_RTS(tty->hardware, tty->secondary_channel_idx, 1); if (ret) return ret; } } if (set & TIOCM_DTR) { ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1); if (ret) return ret; if (tty->secondary_channel_idx != -1) { ret = ipwireless_set_DTR(tty->hardware, tty->secondary_channel_idx, 1); if (ret) return ret; } } if (clear & TIOCM_RTS) { ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0); if (tty->secondary_channel_idx != -1) { ret = ipwireless_set_RTS(tty->hardware, tty->secondary_channel_idx, 0); if (ret) return ret; } } if (clear & TIOCM_DTR) { ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0); if (tty->secondary_channel_idx != -1) { ret = ipwireless_set_DTR(tty->hardware, tty->secondary_channel_idx, 0); if (ret) return ret; } } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba263100.00%1100.00%
Total263100.00%1100.00%


static int ipw_tiocmget(struct tty_struct *linux_tty) { struct ipw_tty *tty = linux_tty->driver_data; /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; if (!tty->port.count) return -EINVAL; return get_control_lines(tty); }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba4591.84%133.33%
jiri slabyjiri slaby36.12%133.33%
alan coxalan cox12.04%133.33%
Total49100.00%3100.00%


static int ipw_tiocmset(struct tty_struct *linux_tty, unsigned int set, unsigned int clear) { struct ipw_tty *tty = linux_tty->driver_data; /* FIXME: Exactly how is the tty object locked here .. */ if (!tty) return -ENODEV; if (!tty->port.count) return -EINVAL; return set_control_lines(tty, set, clear); }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba5793.44%133.33%
jiri slabyjiri slaby34.92%133.33%
alan coxalan cox11.64%133.33%
Total61100.00%3100.00%


static int ipw_ioctl(struct tty_struct *linux_tty, unsigned int cmd, unsigned long arg) { struct ipw_tty *tty = linux_tty->driver_data; if (!tty) return -ENODEV; if (!tty->port.count) return -EINVAL; /* FIXME: Exactly how is the tty object locked here .. */ switch (cmd) { case TIOCGSERIAL: return ipwireless_get_serial_info(tty, (void __user *) arg); case TIOCSSERIAL: return 0; /* Keeps the PCMCIA scripts happy. */ } if (tty->tty_type == TTYTYPE_MODEM) { switch (cmd) { case PPPIOCGCHAN: { int chan = ipwireless_ppp_channel_index( tty->network); if (chan < 0) return -ENODEV; if (put_user(chan, (int __user *) arg)) return -EFAULT; } return 0; case PPPIOCGUNIT: { int unit = ipwireless_ppp_unit_number( tty->network); if (unit < 0) return -ENODEV; if (put_user(unit, (int __user *) arg)) return -EFAULT; } return 0; case FIONREAD: { int val = 0; if (put_user(val, (int __user *) arg)) return -EFAULT; } return 0; case TCFLSH: return tty_perform_flush(linux_tty, arg); } } return -ENOIOCTLCMD; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba21792.74%125.00%
alan coxalan cox145.98%250.00%
jiri slabyjiri slaby31.28%125.00%
Total234100.00%4100.00%


static int add_tty(int j, struct ipw_hardware *hardware, struct ipw_network *network, int channel_idx, int secondary_channel_idx, int tty_type) { ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL); if (!ttys[j]) return -ENOMEM; ttys[j]->index = j; ttys[j]->hardware = hardware; ttys[j]->channel_idx = channel_idx; ttys[j]->secondary_channel_idx = secondary_channel_idx; ttys[j]->network = network; ttys[j]->tty_type = tty_type; mutex_init(&ttys[j]->ipw_tty_mutex); tty_port_init(&ttys[j]->port); tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL); ipwireless_associate_network_tty(network, channel_idx, ttys[j]); if (secondary_channel_idx != -1) ipwireless_associate_network_tty(network, secondary_channel_idx, ttys[j]); /* check if we provide raw device (if loopback is enabled) */ if (get_tty(j)) printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": registering %s device ttyIPWp%d\n", tty_type_name(tty_type), j); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba17285.57%125.00%
jiri slabyjiri slaby2914.43%375.00%
Total201100.00%4100.00%


struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware, struct ipw_network *network) { int i, j; for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) { int allfree = 1; for (j = i; j < IPWIRELESS_PCMCIA_MINORS; j += IPWIRELESS_PCMCIA_MINOR_RANGE) if (ttys[j] != NULL) { allfree = 0; break; } if (allfree) { j = i; if (add_tty(j, hardware, network, IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS, TTYTYPE_MODEM)) return NULL; j += IPWIRELESS_PCMCIA_MINOR_RANGE; if (add_tty(j, hardware, network, IPW_CHANNEL_DIALLER, -1, TTYTYPE_MONITOR)) return NULL; j += IPWIRELESS_PCMCIA_MINOR_RANGE; if (add_tty(j, hardware, network, IPW_CHANNEL_RAS, -1, TTYTYPE_RAS_RAW)) return NULL; return ttys[i]; } } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba161100.00%1100.00%
Total161100.00%1100.00%

/* * Must be called before ipwireless_network_free(). */
void ipwireless_tty_free(struct ipw_tty *tty) { int j; struct ipw_network *network = ttys[tty->index]->network; for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS; j += IPWIRELESS_PCMCIA_MINOR_RANGE) { struct ipw_tty *ttyj = ttys[j]; if (ttyj) { mutex_lock(&ttyj->ipw_tty_mutex); if (get_tty(j)) printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": deregistering %s device ttyIPWp%d\n", tty_type_name(ttyj->tty_type), j); ttyj->closing = 1; if (ttyj->port.tty != NULL) { mutex_unlock(&ttyj->ipw_tty_mutex); tty_vhangup(ttyj->port.tty); /* FIXME: Exactly how is the tty object locked here against a parallel ioctl etc */ /* FIXME2: hangup does not mean all processes * are gone */ mutex_lock(&ttyj->ipw_tty_mutex); } while (ttyj->port.count) do_ipw_close(ttyj); ipwireless_disassociate_network_ttys(network, ttyj->channel_idx); tty_unregister_device(ipw_tty_driver, j); tty_port_destroy(&ttyj->port); ttys[j] = NULL; mutex_unlock(&ttyj->ipw_tty_mutex); kfree(ttyj); } } }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba16085.11%114.29%
jiri slabyjiri slaby2714.36%571.43%
alan coxalan cox10.53%114.29%
Total188100.00%7100.00%

static const struct tty_operations tty_ops = { .open = ipw_open, .close = ipw_close, .hangup = ipw_hangup, .write = ipw_write, .write_room = ipw_write_room, .ioctl = ipw_ioctl, .chars_in_buffer = ipw_chars_in_buffer, .tiocmget = ipw_tiocmget, .tiocmset = ipw_tiocmset, };
int ipwireless_tty_init(void) { int result; ipw_tty_driver = alloc_tty_driver(IPWIRELESS_PCMCIA_MINORS); if (!ipw_tty_driver) return -ENOMEM; ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME; ipw_tty_driver->name = "ttyIPWp"; ipw_tty_driver->major = 0; ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START; ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL; ipw_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ipw_tty_driver->init_termios = tty_std_termios; ipw_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; ipw_tty_driver->init_termios.c_ispeed = 9600; ipw_tty_driver->init_termios.c_ospeed = 9600; tty_set_operations(ipw_tty_driver, &tty_ops); result = tty_register_driver(ipw_tty_driver); if (result) { printk(KERN_ERR IPWIRELESS_PCCARD_NAME ": failed to register tty driver\n"); put_tty_driver(ipw_tty_driver); return result; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba146100.00%1100.00%
Total146100.00%1100.00%


void ipwireless_tty_release(void) { int ret; ret = tty_unregister_driver(ipw_tty_driver); put_tty_driver(ipw_tty_driver); if (ret != 0) printk(KERN_ERR IPWIRELESS_PCCARD_NAME ": tty_unregister_driver failed with code %d\n", ret); }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba36100.00%1100.00%
Total36100.00%1100.00%


int ipwireless_tty_is_modem(struct ipw_tty *tty) { return tty->tty_type == TTYTYPE_MODEM; }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba17100.00%1100.00%
Total17100.00%1100.00%


void ipwireless_tty_notify_control_line_change(struct ipw_tty *tty, unsigned int channel_idx, unsigned int control_lines, unsigned int changed_mask) { unsigned int old_control_lines = tty->control_lines; tty->control_lines = (tty->control_lines & ~changed_mask) | (control_lines & changed_mask); /* * If DCD is de-asserted, we close the tty so pppd can tell that we * have gone offline. */ if ((old_control_lines & IPW_CONTROL_LINE_DCD) && !(tty->control_lines & IPW_CONTROL_LINE_DCD) && tty->port.tty) { tty_hangup(tty->port.tty); } }

Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba7892.86%150.00%
jiri slabyjiri slaby67.14%150.00%
Total84100.00%2100.00%


Overall Contributors

PersonTokensPropCommitsCommitProp
david sterbadavid sterba245394.20%16.25%
jiri slabyjiri slaby1294.95%1062.50%
alan coxalan cox200.77%318.75%
alexey dobriyanalexey dobriyan10.04%16.25%
paul mackerraspaul mackerras10.04%16.25%
Total2604100.00%16100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}