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 };
58282361a0SGreg Kroah-Hartman
59282361a0SGreg Kroah-Hartman static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS];
60282361a0SGreg Kroah-Hartman
61282361a0SGreg Kroah-Hartman static struct tty_driver *ipw_tty_driver;
62282361a0SGreg Kroah-Hartman
tty_type_name(int tty_type)63282361a0SGreg Kroah-Hartman static char *tty_type_name(int tty_type)
64282361a0SGreg Kroah-Hartman {
65282361a0SGreg Kroah-Hartman static char *channel_names[] = {
66282361a0SGreg Kroah-Hartman "modem",
67282361a0SGreg Kroah-Hartman "monitor",
68282361a0SGreg Kroah-Hartman "RAS-raw"
69282361a0SGreg Kroah-Hartman };
70282361a0SGreg Kroah-Hartman
71282361a0SGreg Kroah-Hartman return channel_names[tty_type];
72282361a0SGreg Kroah-Hartman }
73282361a0SGreg Kroah-Hartman
get_tty(int index)74ecaa3bdaSJiri Slaby static struct ipw_tty *get_tty(int index)
75282361a0SGreg Kroah-Hartman {
76282361a0SGreg Kroah-Hartman /*
77282361a0SGreg Kroah-Hartman * The 'ras_raw' channel is only available when 'loopback' mode
78282361a0SGreg Kroah-Hartman * is enabled.
79282361a0SGreg Kroah-Hartman * Number of minor starts with 16 (_RANGE * _RAS_RAW).
80282361a0SGreg Kroah-Hartman */
81ecaa3bdaSJiri Slaby if (!ipwireless_loopback && index >=
82282361a0SGreg Kroah-Hartman IPWIRELESS_PCMCIA_MINOR_RANGE * TTYTYPE_RAS_RAW)
83282361a0SGreg Kroah-Hartman return NULL;
84282361a0SGreg Kroah-Hartman
85ecaa3bdaSJiri Slaby return ttys[index];
86282361a0SGreg Kroah-Hartman }
87282361a0SGreg Kroah-Hartman
ipw_open(struct tty_struct * linux_tty,struct file * filp)88282361a0SGreg Kroah-Hartman static int ipw_open(struct tty_struct *linux_tty, struct file *filp)
89282361a0SGreg Kroah-Hartman {
90ecaa3bdaSJiri Slaby struct ipw_tty *tty = get_tty(linux_tty->index);
91282361a0SGreg Kroah-Hartman
92282361a0SGreg Kroah-Hartman if (!tty)
93282361a0SGreg Kroah-Hartman return -ENODEV;
94282361a0SGreg Kroah-Hartman
95282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex);
967393af80SJiri Slaby if (tty->port.count == 0)
97282361a0SGreg Kroah-Hartman tty->tx_bytes_queued = 0;
98282361a0SGreg Kroah-Hartman
997393af80SJiri Slaby tty->port.count++;
100282361a0SGreg Kroah-Hartman
10119ef1b71SJiri Slaby tty->port.tty = linux_tty;
102282361a0SGreg Kroah-Hartman linux_tty->driver_data = tty;
103282361a0SGreg Kroah-Hartman
104282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM)
105282361a0SGreg Kroah-Hartman ipwireless_ppp_open(tty->network);
106282361a0SGreg Kroah-Hartman
107282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
108282361a0SGreg Kroah-Hartman
109282361a0SGreg Kroah-Hartman return 0;
110282361a0SGreg Kroah-Hartman }
111282361a0SGreg Kroah-Hartman
do_ipw_close(struct ipw_tty * tty)112282361a0SGreg Kroah-Hartman static void do_ipw_close(struct ipw_tty *tty)
113282361a0SGreg Kroah-Hartman {
1147393af80SJiri Slaby tty->port.count--;
115282361a0SGreg Kroah-Hartman
1167393af80SJiri Slaby if (tty->port.count == 0) {
11719ef1b71SJiri Slaby struct tty_struct *linux_tty = tty->port.tty;
118282361a0SGreg Kroah-Hartman
119282361a0SGreg Kroah-Hartman if (linux_tty != NULL) {
12019ef1b71SJiri Slaby tty->port.tty = NULL;
121282361a0SGreg Kroah-Hartman linux_tty->driver_data = NULL;
122282361a0SGreg Kroah-Hartman
123282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM)
124282361a0SGreg Kroah-Hartman ipwireless_ppp_close(tty->network);
125282361a0SGreg Kroah-Hartman }
126282361a0SGreg Kroah-Hartman }
127282361a0SGreg Kroah-Hartman }
128282361a0SGreg Kroah-Hartman
ipw_hangup(struct tty_struct * linux_tty)129282361a0SGreg Kroah-Hartman static void ipw_hangup(struct tty_struct *linux_tty)
130282361a0SGreg Kroah-Hartman {
131282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data;
132282361a0SGreg Kroah-Hartman
133282361a0SGreg Kroah-Hartman if (!tty)
134282361a0SGreg Kroah-Hartman return;
135282361a0SGreg Kroah-Hartman
136282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex);
1377393af80SJiri Slaby if (tty->port.count == 0) {
138282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
139282361a0SGreg Kroah-Hartman return;
140282361a0SGreg Kroah-Hartman }
141282361a0SGreg Kroah-Hartman
142282361a0SGreg Kroah-Hartman do_ipw_close(tty);
143282361a0SGreg Kroah-Hartman
144282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
145282361a0SGreg Kroah-Hartman }
146282361a0SGreg Kroah-Hartman
ipw_close(struct tty_struct * linux_tty,struct file * filp)147282361a0SGreg Kroah-Hartman static void ipw_close(struct tty_struct *linux_tty, struct file *filp)
148282361a0SGreg Kroah-Hartman {
149282361a0SGreg Kroah-Hartman ipw_hangup(linux_tty);
150282361a0SGreg Kroah-Hartman }
151282361a0SGreg Kroah-Hartman
152282361a0SGreg Kroah-Hartman /* Take data received from hardware, and send it out the tty */
ipwireless_tty_received(struct ipw_tty * tty,unsigned char * data,unsigned int length)153282361a0SGreg Kroah-Hartman void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data,
154282361a0SGreg Kroah-Hartman unsigned int length)
155282361a0SGreg Kroah-Hartman {
156282361a0SGreg Kroah-Hartman int work = 0;
157282361a0SGreg Kroah-Hartman
158282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex);
159282361a0SGreg Kroah-Hartman
1607393af80SJiri Slaby if (!tty->port.count) {
161282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
162282361a0SGreg Kroah-Hartman return;
163282361a0SGreg Kroah-Hartman }
164282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
165282361a0SGreg Kroah-Hartman
16605c7cd39SJiri Slaby work = tty_insert_flip_string(&tty->port, data, length);
167282361a0SGreg Kroah-Hartman
168282361a0SGreg Kroah-Hartman if (work != length)
169282361a0SGreg Kroah-Hartman printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
170282361a0SGreg Kroah-Hartman ": %d chars not inserted to flip buffer!\n",
171282361a0SGreg Kroah-Hartman length - work);
172282361a0SGreg Kroah-Hartman
173282361a0SGreg Kroah-Hartman if (work)
1742e124b4aSJiri Slaby tty_flip_buffer_push(&tty->port);
175282361a0SGreg Kroah-Hartman }
176282361a0SGreg Kroah-Hartman
ipw_write_packet_sent_callback(void * callback_data,unsigned int packet_length)177282361a0SGreg Kroah-Hartman static void ipw_write_packet_sent_callback(void *callback_data,
178282361a0SGreg Kroah-Hartman unsigned int packet_length)
179282361a0SGreg Kroah-Hartman {
180282361a0SGreg Kroah-Hartman struct ipw_tty *tty = callback_data;
181282361a0SGreg Kroah-Hartman
182282361a0SGreg Kroah-Hartman /*
183282361a0SGreg Kroah-Hartman * Packet has been sent, so we subtract the number of bytes from our
184282361a0SGreg Kroah-Hartman * tally of outstanding TX bytes.
185282361a0SGreg Kroah-Hartman */
186282361a0SGreg Kroah-Hartman tty->tx_bytes_queued -= packet_length;
187282361a0SGreg Kroah-Hartman }
188282361a0SGreg Kroah-Hartman
ipw_write(struct tty_struct * linux_tty,const u8 * buf,size_t count)189*95713967SJiri Slaby (SUSE) static ssize_t ipw_write(struct tty_struct *linux_tty, const u8 *buf,
190*95713967SJiri Slaby (SUSE) size_t count)
191282361a0SGreg Kroah-Hartman {
192282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data;
193282361a0SGreg Kroah-Hartman int room, ret;
194282361a0SGreg Kroah-Hartman
195282361a0SGreg Kroah-Hartman if (!tty)
196282361a0SGreg Kroah-Hartman return -ENODEV;
197282361a0SGreg Kroah-Hartman
198282361a0SGreg Kroah-Hartman mutex_lock(&tty->ipw_tty_mutex);
1997393af80SJiri Slaby if (!tty->port.count) {
200282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
201282361a0SGreg Kroah-Hartman return -EINVAL;
202282361a0SGreg Kroah-Hartman }
203282361a0SGreg Kroah-Hartman
204282361a0SGreg Kroah-Hartman room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
205282361a0SGreg Kroah-Hartman if (room < 0)
206282361a0SGreg Kroah-Hartman room = 0;
207282361a0SGreg Kroah-Hartman /* Don't allow caller to write any more than we have room for */
208282361a0SGreg Kroah-Hartman if (count > room)
209282361a0SGreg Kroah-Hartman count = room;
210282361a0SGreg Kroah-Hartman
211282361a0SGreg Kroah-Hartman if (count == 0) {
212282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
213282361a0SGreg Kroah-Hartman return 0;
214282361a0SGreg Kroah-Hartman }
215282361a0SGreg Kroah-Hartman
216282361a0SGreg Kroah-Hartman ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS,
217282361a0SGreg Kroah-Hartman buf, count,
218282361a0SGreg Kroah-Hartman ipw_write_packet_sent_callback, tty);
219db332356STong Zhang if (ret < 0) {
220282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
221282361a0SGreg Kroah-Hartman return 0;
222282361a0SGreg Kroah-Hartman }
223282361a0SGreg Kroah-Hartman
224282361a0SGreg Kroah-Hartman tty->tx_bytes_queued += count;
225282361a0SGreg Kroah-Hartman mutex_unlock(&tty->ipw_tty_mutex);
226282361a0SGreg Kroah-Hartman
227282361a0SGreg Kroah-Hartman return count;
228282361a0SGreg Kroah-Hartman }
229282361a0SGreg Kroah-Hartman
ipw_write_room(struct tty_struct * linux_tty)23003b3b1a2SJiri Slaby static unsigned int ipw_write_room(struct tty_struct *linux_tty)
231282361a0SGreg Kroah-Hartman {
232282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data;
233282361a0SGreg Kroah-Hartman int room;
234282361a0SGreg Kroah-Hartman
235282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */
236282361a0SGreg Kroah-Hartman if (!tty)
2376bfbfcfcSJiri Slaby return 0;
238282361a0SGreg Kroah-Hartman
2397393af80SJiri Slaby if (!tty->port.count)
2406bfbfcfcSJiri Slaby return 0;
241282361a0SGreg Kroah-Hartman
242282361a0SGreg Kroah-Hartman room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
243282361a0SGreg Kroah-Hartman if (room < 0)
244282361a0SGreg Kroah-Hartman room = 0;
245282361a0SGreg Kroah-Hartman
246282361a0SGreg Kroah-Hartman return room;
247282361a0SGreg Kroah-Hartman }
248282361a0SGreg Kroah-Hartman
ipwireless_get_serial_info(struct tty_struct * linux_tty,struct serial_struct * ss)249a7b06fcfSAl Viro static int ipwireless_get_serial_info(struct tty_struct *linux_tty,
250a7b06fcfSAl Viro struct serial_struct *ss)
251282361a0SGreg Kroah-Hartman {
252a7b06fcfSAl Viro struct ipw_tty *tty = linux_tty->driver_data;
253282361a0SGreg Kroah-Hartman
254a7b06fcfSAl Viro if (!tty)
255a7b06fcfSAl Viro return -ENODEV;
256fec4daecSJiri Slaby
257a7b06fcfSAl Viro if (!tty->port.count)
258a7b06fcfSAl Viro return -EINVAL;
259282361a0SGreg Kroah-Hartman
260a7b06fcfSAl Viro ss->type = PORT_UNKNOWN;
261a7b06fcfSAl Viro ss->line = tty->index;
262a7b06fcfSAl Viro ss->baud_base = 115200;
263282361a0SGreg Kroah-Hartman return 0;
264282361a0SGreg Kroah-Hartman }
265282361a0SGreg Kroah-Hartman
ipwireless_set_serial_info(struct tty_struct * linux_tty,struct serial_struct * ss)266a7b06fcfSAl Viro static int ipwireless_set_serial_info(struct tty_struct *linux_tty,
267a7b06fcfSAl Viro struct serial_struct *ss)
268a7b06fcfSAl Viro {
269a7b06fcfSAl Viro return 0; /* Keeps the PCMCIA scripts happy. */
270a7b06fcfSAl Viro }
271a7b06fcfSAl Viro
ipw_chars_in_buffer(struct tty_struct * linux_tty)272fff4ef17SJiri Slaby static unsigned int ipw_chars_in_buffer(struct tty_struct *linux_tty)
273282361a0SGreg Kroah-Hartman {
274282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data;
275282361a0SGreg Kroah-Hartman
276282361a0SGreg Kroah-Hartman if (!tty)
277282361a0SGreg Kroah-Hartman return 0;
278282361a0SGreg Kroah-Hartman
2797393af80SJiri Slaby if (!tty->port.count)
280282361a0SGreg Kroah-Hartman return 0;
281282361a0SGreg Kroah-Hartman
282282361a0SGreg Kroah-Hartman return tty->tx_bytes_queued;
283282361a0SGreg Kroah-Hartman }
284282361a0SGreg Kroah-Hartman
get_control_lines(struct ipw_tty * tty)285282361a0SGreg Kroah-Hartman static int get_control_lines(struct ipw_tty *tty)
286282361a0SGreg Kroah-Hartman {
287282361a0SGreg Kroah-Hartman unsigned int my = tty->control_lines;
288282361a0SGreg Kroah-Hartman unsigned int out = 0;
289282361a0SGreg Kroah-Hartman
290282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_RTS)
291282361a0SGreg Kroah-Hartman out |= TIOCM_RTS;
292282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DTR)
293282361a0SGreg Kroah-Hartman out |= TIOCM_DTR;
294282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_CTS)
295282361a0SGreg Kroah-Hartman out |= TIOCM_CTS;
296282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DSR)
297282361a0SGreg Kroah-Hartman out |= TIOCM_DSR;
298282361a0SGreg Kroah-Hartman if (my & IPW_CONTROL_LINE_DCD)
299282361a0SGreg Kroah-Hartman out |= TIOCM_CD;
300282361a0SGreg Kroah-Hartman
301282361a0SGreg Kroah-Hartman return out;
302282361a0SGreg Kroah-Hartman }
303282361a0SGreg Kroah-Hartman
set_control_lines(struct ipw_tty * tty,unsigned int set,unsigned int clear)304282361a0SGreg Kroah-Hartman static int set_control_lines(struct ipw_tty *tty, unsigned int set,
305282361a0SGreg Kroah-Hartman unsigned int clear)
306282361a0SGreg Kroah-Hartman {
307282361a0SGreg Kroah-Hartman int ret;
308282361a0SGreg Kroah-Hartman
309282361a0SGreg Kroah-Hartman if (set & TIOCM_RTS) {
310282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1);
311282361a0SGreg Kroah-Hartman if (ret)
312282361a0SGreg Kroah-Hartman return ret;
313282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) {
314282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware,
315282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 1);
316282361a0SGreg Kroah-Hartman if (ret)
317282361a0SGreg Kroah-Hartman return ret;
318282361a0SGreg Kroah-Hartman }
319282361a0SGreg Kroah-Hartman }
320282361a0SGreg Kroah-Hartman if (set & TIOCM_DTR) {
321282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1);
322282361a0SGreg Kroah-Hartman if (ret)
323282361a0SGreg Kroah-Hartman return ret;
324282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) {
325282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware,
326282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 1);
327282361a0SGreg Kroah-Hartman if (ret)
328282361a0SGreg Kroah-Hartman return ret;
329282361a0SGreg Kroah-Hartman }
330282361a0SGreg Kroah-Hartman }
331282361a0SGreg Kroah-Hartman if (clear & TIOCM_RTS) {
332282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0);
333282361a0SGreg Kroah-Hartman if (tty->secondary_channel_idx != -1) {
334282361a0SGreg Kroah-Hartman ret = ipwireless_set_RTS(tty->hardware,
335282361a0SGreg Kroah-Hartman tty->secondary_channel_idx, 0);
336282361a0SGreg Kroah-Hartman if (ret)
337282361a0SGreg Kroah-Hartman return ret;
338282361a0SGreg Kroah-Hartman }
339282361a0SGreg Kroah-Hartman }
340282361a0SGreg Kroah-Hartman if (clear & TIOCM_DTR) {
341282361a0SGreg Kroah-Hartman ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0);
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, 0);
345282361a0SGreg Kroah-Hartman if (ret)
346282361a0SGreg Kroah-Hartman return ret;
347282361a0SGreg Kroah-Hartman }
348282361a0SGreg Kroah-Hartman }
349282361a0SGreg Kroah-Hartman return 0;
350282361a0SGreg Kroah-Hartman }
351282361a0SGreg Kroah-Hartman
ipw_tiocmget(struct tty_struct * linux_tty)352282361a0SGreg Kroah-Hartman static int ipw_tiocmget(struct tty_struct *linux_tty)
353282361a0SGreg Kroah-Hartman {
354282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data;
355282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */
356282361a0SGreg Kroah-Hartman
357282361a0SGreg Kroah-Hartman if (!tty)
358282361a0SGreg Kroah-Hartman return -ENODEV;
359282361a0SGreg Kroah-Hartman
3607393af80SJiri Slaby if (!tty->port.count)
361282361a0SGreg Kroah-Hartman return -EINVAL;
362282361a0SGreg Kroah-Hartman
363282361a0SGreg Kroah-Hartman return get_control_lines(tty);
364282361a0SGreg Kroah-Hartman }
365282361a0SGreg Kroah-Hartman
366282361a0SGreg Kroah-Hartman static int
ipw_tiocmset(struct tty_struct * linux_tty,unsigned int set,unsigned int clear)367282361a0SGreg Kroah-Hartman ipw_tiocmset(struct tty_struct *linux_tty,
368282361a0SGreg Kroah-Hartman unsigned int set, unsigned int clear)
369282361a0SGreg Kroah-Hartman {
370282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data;
371282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */
372282361a0SGreg Kroah-Hartman
373282361a0SGreg Kroah-Hartman if (!tty)
374282361a0SGreg Kroah-Hartman return -ENODEV;
375282361a0SGreg Kroah-Hartman
3767393af80SJiri Slaby if (!tty->port.count)
377282361a0SGreg Kroah-Hartman return -EINVAL;
378282361a0SGreg Kroah-Hartman
379282361a0SGreg Kroah-Hartman return set_control_lines(tty, set, clear);
380282361a0SGreg Kroah-Hartman }
381282361a0SGreg Kroah-Hartman
ipw_ioctl(struct tty_struct * linux_tty,unsigned int cmd,unsigned long arg)382282361a0SGreg Kroah-Hartman static int ipw_ioctl(struct tty_struct *linux_tty,
383282361a0SGreg Kroah-Hartman unsigned int cmd, unsigned long arg)
384282361a0SGreg Kroah-Hartman {
385282361a0SGreg Kroah-Hartman struct ipw_tty *tty = linux_tty->driver_data;
386282361a0SGreg Kroah-Hartman
387282361a0SGreg Kroah-Hartman if (!tty)
388282361a0SGreg Kroah-Hartman return -ENODEV;
389282361a0SGreg Kroah-Hartman
3907393af80SJiri Slaby if (!tty->port.count)
391282361a0SGreg Kroah-Hartman return -EINVAL;
392282361a0SGreg Kroah-Hartman
393282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here .. */
394282361a0SGreg Kroah-Hartman if (tty->tty_type == TTYTYPE_MODEM) {
395282361a0SGreg Kroah-Hartman switch (cmd) {
396282361a0SGreg Kroah-Hartman case PPPIOCGCHAN:
397282361a0SGreg Kroah-Hartman {
398282361a0SGreg Kroah-Hartman int chan = ipwireless_ppp_channel_index(
399282361a0SGreg Kroah-Hartman tty->network);
400282361a0SGreg Kroah-Hartman
401282361a0SGreg Kroah-Hartman if (chan < 0)
402282361a0SGreg Kroah-Hartman return -ENODEV;
403282361a0SGreg Kroah-Hartman if (put_user(chan, (int __user *) arg))
404282361a0SGreg Kroah-Hartman return -EFAULT;
405282361a0SGreg Kroah-Hartman }
406282361a0SGreg Kroah-Hartman return 0;
407282361a0SGreg Kroah-Hartman
408282361a0SGreg Kroah-Hartman case PPPIOCGUNIT:
409282361a0SGreg Kroah-Hartman {
410282361a0SGreg Kroah-Hartman int unit = ipwireless_ppp_unit_number(
411282361a0SGreg Kroah-Hartman tty->network);
412282361a0SGreg Kroah-Hartman
413282361a0SGreg Kroah-Hartman if (unit < 0)
414282361a0SGreg Kroah-Hartman return -ENODEV;
415282361a0SGreg Kroah-Hartman if (put_user(unit, (int __user *) arg))
416282361a0SGreg Kroah-Hartman return -EFAULT;
417282361a0SGreg Kroah-Hartman }
418282361a0SGreg Kroah-Hartman return 0;
419282361a0SGreg Kroah-Hartman
420282361a0SGreg Kroah-Hartman case FIONREAD:
421282361a0SGreg Kroah-Hartman {
422282361a0SGreg Kroah-Hartman int val = 0;
423282361a0SGreg Kroah-Hartman
424282361a0SGreg Kroah-Hartman if (put_user(val, (int __user *) arg))
425282361a0SGreg Kroah-Hartman return -EFAULT;
426282361a0SGreg Kroah-Hartman }
427282361a0SGreg Kroah-Hartman return 0;
428282361a0SGreg Kroah-Hartman case TCFLSH:
429282361a0SGreg Kroah-Hartman return tty_perform_flush(linux_tty, arg);
430282361a0SGreg Kroah-Hartman }
431282361a0SGreg Kroah-Hartman }
432282361a0SGreg Kroah-Hartman return -ENOIOCTLCMD;
433282361a0SGreg Kroah-Hartman }
434282361a0SGreg Kroah-Hartman
add_tty(int j,struct ipw_hardware * hardware,struct ipw_network * network,int channel_idx,int secondary_channel_idx,int tty_type)435282361a0SGreg Kroah-Hartman static int add_tty(int j,
436282361a0SGreg Kroah-Hartman struct ipw_hardware *hardware,
437282361a0SGreg Kroah-Hartman struct ipw_network *network, int channel_idx,
438282361a0SGreg Kroah-Hartman int secondary_channel_idx, int tty_type)
439282361a0SGreg Kroah-Hartman {
440282361a0SGreg Kroah-Hartman ttys[j] = kzalloc(sizeof(struct ipw_tty), GFP_KERNEL);
441282361a0SGreg Kroah-Hartman if (!ttys[j])
442282361a0SGreg Kroah-Hartman return -ENOMEM;
443282361a0SGreg Kroah-Hartman ttys[j]->index = j;
444282361a0SGreg Kroah-Hartman ttys[j]->hardware = hardware;
445282361a0SGreg Kroah-Hartman ttys[j]->channel_idx = channel_idx;
446282361a0SGreg Kroah-Hartman ttys[j]->secondary_channel_idx = secondary_channel_idx;
447282361a0SGreg Kroah-Hartman ttys[j]->network = network;
448282361a0SGreg Kroah-Hartman ttys[j]->tty_type = tty_type;
449282361a0SGreg Kroah-Hartman mutex_init(&ttys[j]->ipw_tty_mutex);
4507393af80SJiri Slaby tty_port_init(&ttys[j]->port);
451282361a0SGreg Kroah-Hartman
452734cc178SJiri Slaby tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL);
453282361a0SGreg Kroah-Hartman ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
454282361a0SGreg Kroah-Hartman
455282361a0SGreg Kroah-Hartman if (secondary_channel_idx != -1)
456282361a0SGreg Kroah-Hartman ipwireless_associate_network_tty(network,
457282361a0SGreg Kroah-Hartman secondary_channel_idx,
458282361a0SGreg Kroah-Hartman ttys[j]);
459e6df3cceSJiri Slaby /* check if we provide raw device (if loopback is enabled) */
460e6df3cceSJiri Slaby if (get_tty(j))
461e6df3cceSJiri Slaby printk(KERN_INFO IPWIRELESS_PCCARD_NAME
462e6df3cceSJiri Slaby ": registering %s device ttyIPWp%d\n",
463e6df3cceSJiri Slaby tty_type_name(tty_type), j);
464e6df3cceSJiri Slaby
465282361a0SGreg Kroah-Hartman return 0;
466282361a0SGreg Kroah-Hartman }
467282361a0SGreg Kroah-Hartman
ipwireless_tty_create(struct ipw_hardware * hardware,struct ipw_network * network)468282361a0SGreg Kroah-Hartman struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware,
469282361a0SGreg Kroah-Hartman struct ipw_network *network)
470282361a0SGreg Kroah-Hartman {
471282361a0SGreg Kroah-Hartman int i, j;
472282361a0SGreg Kroah-Hartman
473282361a0SGreg Kroah-Hartman for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) {
474282361a0SGreg Kroah-Hartman int allfree = 1;
475282361a0SGreg Kroah-Hartman
476282361a0SGreg Kroah-Hartman for (j = i; j < IPWIRELESS_PCMCIA_MINORS;
477282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE)
478282361a0SGreg Kroah-Hartman if (ttys[j] != NULL) {
479282361a0SGreg Kroah-Hartman allfree = 0;
480282361a0SGreg Kroah-Hartman break;
481282361a0SGreg Kroah-Hartman }
482282361a0SGreg Kroah-Hartman
483282361a0SGreg Kroah-Hartman if (allfree) {
484282361a0SGreg Kroah-Hartman j = i;
485282361a0SGreg Kroah-Hartman
486282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network,
487282361a0SGreg Kroah-Hartman IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS,
488282361a0SGreg Kroah-Hartman TTYTYPE_MODEM))
489282361a0SGreg Kroah-Hartman return NULL;
490282361a0SGreg Kroah-Hartman
491282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE;
492282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network,
493282361a0SGreg Kroah-Hartman IPW_CHANNEL_DIALLER, -1,
494282361a0SGreg Kroah-Hartman TTYTYPE_MONITOR))
495282361a0SGreg Kroah-Hartman return NULL;
496282361a0SGreg Kroah-Hartman
497282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE;
498282361a0SGreg Kroah-Hartman if (add_tty(j, hardware, network,
499282361a0SGreg Kroah-Hartman IPW_CHANNEL_RAS, -1,
500282361a0SGreg Kroah-Hartman TTYTYPE_RAS_RAW))
501282361a0SGreg Kroah-Hartman return NULL;
502282361a0SGreg Kroah-Hartman
503282361a0SGreg Kroah-Hartman return ttys[i];
504282361a0SGreg Kroah-Hartman }
505282361a0SGreg Kroah-Hartman }
506282361a0SGreg Kroah-Hartman return NULL;
507282361a0SGreg Kroah-Hartman }
508282361a0SGreg Kroah-Hartman
509282361a0SGreg Kroah-Hartman /*
510282361a0SGreg Kroah-Hartman * Must be called before ipwireless_network_free().
511282361a0SGreg Kroah-Hartman */
ipwireless_tty_free(struct ipw_tty * tty)512282361a0SGreg Kroah-Hartman void ipwireless_tty_free(struct ipw_tty *tty)
513282361a0SGreg Kroah-Hartman {
514282361a0SGreg Kroah-Hartman int j;
515282361a0SGreg Kroah-Hartman struct ipw_network *network = ttys[tty->index]->network;
516282361a0SGreg Kroah-Hartman
517282361a0SGreg Kroah-Hartman for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS;
518282361a0SGreg Kroah-Hartman j += IPWIRELESS_PCMCIA_MINOR_RANGE) {
519282361a0SGreg Kroah-Hartman struct ipw_tty *ttyj = ttys[j];
520282361a0SGreg Kroah-Hartman
521282361a0SGreg Kroah-Hartman if (ttyj) {
522282361a0SGreg Kroah-Hartman mutex_lock(&ttyj->ipw_tty_mutex);
523e6df3cceSJiri Slaby if (get_tty(j))
524e6df3cceSJiri Slaby printk(KERN_INFO IPWIRELESS_PCCARD_NAME
525e6df3cceSJiri Slaby ": deregistering %s device ttyIPWp%d\n",
526e6df3cceSJiri Slaby tty_type_name(ttyj->tty_type), j);
52719ef1b71SJiri Slaby if (ttyj->port.tty != NULL) {
528282361a0SGreg Kroah-Hartman mutex_unlock(&ttyj->ipw_tty_mutex);
52919ef1b71SJiri Slaby tty_vhangup(ttyj->port.tty);
530282361a0SGreg Kroah-Hartman /* FIXME: Exactly how is the tty object locked here
531282361a0SGreg Kroah-Hartman against a parallel ioctl etc */
532de3a60a3SJiri Slaby /* FIXME2: hangup does not mean all processes
533de3a60a3SJiri Slaby * are gone */
534282361a0SGreg Kroah-Hartman mutex_lock(&ttyj->ipw_tty_mutex);
535282361a0SGreg Kroah-Hartman }
5367393af80SJiri Slaby while (ttyj->port.count)
537282361a0SGreg Kroah-Hartman do_ipw_close(ttyj);
538282361a0SGreg Kroah-Hartman ipwireless_disassociate_network_ttys(network,
539282361a0SGreg Kroah-Hartman ttyj->channel_idx);
540282361a0SGreg Kroah-Hartman tty_unregister_device(ipw_tty_driver, j);
541191c5f10SJiri Slaby tty_port_destroy(&ttyj->port);
542282361a0SGreg Kroah-Hartman ttys[j] = NULL;
543282361a0SGreg Kroah-Hartman mutex_unlock(&ttyj->ipw_tty_mutex);
544282361a0SGreg Kroah-Hartman kfree(ttyj);
545282361a0SGreg Kroah-Hartman }
546282361a0SGreg Kroah-Hartman }
547282361a0SGreg Kroah-Hartman }
548282361a0SGreg Kroah-Hartman
549282361a0SGreg Kroah-Hartman static const struct tty_operations tty_ops = {
550282361a0SGreg Kroah-Hartman .open = ipw_open,
551282361a0SGreg Kroah-Hartman .close = ipw_close,
552282361a0SGreg Kroah-Hartman .hangup = ipw_hangup,
553282361a0SGreg Kroah-Hartman .write = ipw_write,
554282361a0SGreg Kroah-Hartman .write_room = ipw_write_room,
555282361a0SGreg Kroah-Hartman .ioctl = ipw_ioctl,
556282361a0SGreg Kroah-Hartman .chars_in_buffer = ipw_chars_in_buffer,
557282361a0SGreg Kroah-Hartman .tiocmget = ipw_tiocmget,
558282361a0SGreg Kroah-Hartman .tiocmset = ipw_tiocmset,
559a7b06fcfSAl Viro .set_serial = ipwireless_set_serial_info,
560a7b06fcfSAl Viro .get_serial = ipwireless_get_serial_info,
561282361a0SGreg Kroah-Hartman };
562282361a0SGreg Kroah-Hartman
ipwireless_tty_init(void)563282361a0SGreg Kroah-Hartman int ipwireless_tty_init(void)
564282361a0SGreg Kroah-Hartman {
565282361a0SGreg Kroah-Hartman int result;
566282361a0SGreg Kroah-Hartman
56739b7b42bSJiri Slaby ipw_tty_driver = tty_alloc_driver(IPWIRELESS_PCMCIA_MINORS,
56839b7b42bSJiri Slaby TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
56939b7b42bSJiri Slaby if (IS_ERR(ipw_tty_driver))
57039b7b42bSJiri Slaby return PTR_ERR(ipw_tty_driver);
571282361a0SGreg Kroah-Hartman
572282361a0SGreg Kroah-Hartman ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME;
573282361a0SGreg Kroah-Hartman ipw_tty_driver->name = "ttyIPWp";
574282361a0SGreg Kroah-Hartman ipw_tty_driver->major = 0;
575282361a0SGreg Kroah-Hartman ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START;
576282361a0SGreg Kroah-Hartman ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
577282361a0SGreg Kroah-Hartman ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL;
578282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios = tty_std_termios;
579282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_cflag =
580282361a0SGreg Kroah-Hartman B9600 | CS8 | CREAD | HUPCL | CLOCAL;
581282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_ispeed = 9600;
582282361a0SGreg Kroah-Hartman ipw_tty_driver->init_termios.c_ospeed = 9600;
583282361a0SGreg Kroah-Hartman tty_set_operations(ipw_tty_driver, &tty_ops);
584282361a0SGreg Kroah-Hartman result = tty_register_driver(ipw_tty_driver);
585282361a0SGreg Kroah-Hartman if (result) {
586282361a0SGreg Kroah-Hartman printk(KERN_ERR IPWIRELESS_PCCARD_NAME
587282361a0SGreg Kroah-Hartman ": failed to register tty driver\n");
5889f90a4ddSJiri Slaby tty_driver_kref_put(ipw_tty_driver);
589282361a0SGreg Kroah-Hartman return result;
590282361a0SGreg Kroah-Hartman }
591282361a0SGreg Kroah-Hartman
592282361a0SGreg Kroah-Hartman return 0;
593282361a0SGreg Kroah-Hartman }
594282361a0SGreg Kroah-Hartman
ipwireless_tty_release(void)595282361a0SGreg Kroah-Hartman void ipwireless_tty_release(void)
596282361a0SGreg Kroah-Hartman {
5976c2e6317SJiri Slaby tty_unregister_driver(ipw_tty_driver);
5989f90a4ddSJiri Slaby tty_driver_kref_put(ipw_tty_driver);
599282361a0SGreg Kroah-Hartman }
600282361a0SGreg Kroah-Hartman
ipwireless_tty_is_modem(struct ipw_tty * tty)601282361a0SGreg Kroah-Hartman int ipwireless_tty_is_modem(struct ipw_tty *tty)
602282361a0SGreg Kroah-Hartman {
603282361a0SGreg Kroah-Hartman return tty->tty_type == TTYTYPE_MODEM;
604282361a0SGreg Kroah-Hartman }
605282361a0SGreg Kroah-Hartman
606282361a0SGreg Kroah-Hartman void
ipwireless_tty_notify_control_line_change(struct ipw_tty * tty,unsigned int channel_idx,unsigned int control_lines,unsigned int changed_mask)607282361a0SGreg Kroah-Hartman ipwireless_tty_notify_control_line_change(struct ipw_tty *tty,
608282361a0SGreg Kroah-Hartman unsigned int channel_idx,
609282361a0SGreg Kroah-Hartman unsigned int control_lines,
610282361a0SGreg Kroah-Hartman unsigned int changed_mask)
611282361a0SGreg Kroah-Hartman {
612282361a0SGreg Kroah-Hartman unsigned int old_control_lines = tty->control_lines;
613282361a0SGreg Kroah-Hartman
614282361a0SGreg Kroah-Hartman tty->control_lines = (tty->control_lines & ~changed_mask)
615282361a0SGreg Kroah-Hartman | (control_lines & changed_mask);
616282361a0SGreg Kroah-Hartman
617282361a0SGreg Kroah-Hartman /*
618282361a0SGreg Kroah-Hartman * If DCD is de-asserted, we close the tty so pppd can tell that we
619282361a0SGreg Kroah-Hartman * have gone offline.
620282361a0SGreg Kroah-Hartman */
621282361a0SGreg Kroah-Hartman if ((old_control_lines & IPW_CONTROL_LINE_DCD)
622282361a0SGreg Kroah-Hartman && !(tty->control_lines & IPW_CONTROL_LINE_DCD)
62319ef1b71SJiri Slaby && tty->port.tty) {
62419ef1b71SJiri Slaby tty_hangup(tty->port.tty);
625282361a0SGreg Kroah-Hartman }
626282361a0SGreg Kroah-Hartman }
627282361a0SGreg Kroah-Hartman
628