diff options
author | Hansjoerg Lipp <hjlipp@web.de> | 2006-03-26 04:38:32 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-26 11:57:05 -0500 |
commit | ee8a4b7f857fe7ba243e65c8925798cf8eda5ab0 (patch) | |
tree | 8803a10dd343d13940ebfc34ce633dbacbefcd4e /drivers/isdn | |
parent | 3c66a22545dd88356bbf4efa45a7b178df8154e9 (diff) |
[PATCH] isdn4linux: Siemens Gigaset drivers - tty interface
And: Tilman Schmidt <tilman@imap.cc>
This patch adds the tty interface to the gigaset module. The tty interface
provides direct access to the AT command set of the Gigaset devices.
Signed-off-by: Hansjoerg Lipp <hjlipp@web.de>
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
Cc: Karsten Keil <kkeil@suse.de>
Cc: Greg KH <greg@kroah.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'drivers/isdn')
-rw-r--r-- | drivers/isdn/gigaset/interface.c | 718 |
1 files changed, 718 insertions, 0 deletions
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c new file mode 100644 index 000000000000..3a81d9c65141 --- /dev/null +++ b/drivers/isdn/gigaset/interface.c | |||
@@ -0,0 +1,718 @@ | |||
1 | /* | ||
2 | * interface to user space for the gigaset driver | ||
3 | * | ||
4 | * Copyright (c) 2004 by Hansjoerg Lipp <hjlipp@web.de> | ||
5 | * | ||
6 | * ===================================================================== | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License as | ||
9 | * published by the Free Software Foundation; either version 2 of | ||
10 | * the License, or (at your option) any later version. | ||
11 | * ===================================================================== | ||
12 | * Version: $Id: interface.c,v 1.14.4.15 2006/02/04 18:28:16 hjlipp Exp $ | ||
13 | * ===================================================================== | ||
14 | */ | ||
15 | |||
16 | #include "gigaset.h" | ||
17 | #include <linux/gigaset_dev.h> | ||
18 | #include <linux/tty.h> | ||
19 | #include <linux/tty_flip.h> | ||
20 | |||
21 | /*** our ioctls ***/ | ||
22 | |||
23 | static int if_lock(struct cardstate *cs, int *arg) | ||
24 | { | ||
25 | int cmd = *arg; | ||
26 | |||
27 | dbg(DEBUG_IF, "%u: if_lock (%d)", cs->minor_index, cmd); | ||
28 | |||
29 | if (cmd > 1) | ||
30 | return -EINVAL; | ||
31 | |||
32 | if (cmd < 0) { | ||
33 | *arg = atomic_read(&cs->mstate) == MS_LOCKED; //FIXME remove? | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | if (!cmd && atomic_read(&cs->mstate) == MS_LOCKED | ||
38 | && atomic_read(&cs->connected)) { | ||
39 | cs->ops->set_modem_ctrl(cs, 0, TIOCM_DTR|TIOCM_RTS); | ||
40 | cs->ops->baud_rate(cs, B115200); | ||
41 | cs->ops->set_line_ctrl(cs, CS8); | ||
42 | cs->control_state = TIOCM_DTR|TIOCM_RTS; | ||
43 | } | ||
44 | |||
45 | cs->waiting = 1; | ||
46 | if (!gigaset_add_event(cs, &cs->at_state, EV_IF_LOCK, | ||
47 | NULL, cmd, NULL)) { | ||
48 | cs->waiting = 0; | ||
49 | return -ENOMEM; | ||
50 | } | ||
51 | |||
52 | dbg(DEBUG_CMD, "scheduling IF_LOCK"); | ||
53 | gigaset_schedule_event(cs); | ||
54 | |||
55 | wait_event(cs->waitqueue, !cs->waiting); | ||
56 | |||
57 | if (cs->cmd_result >= 0) { | ||
58 | *arg = cs->cmd_result; | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | return cs->cmd_result; | ||
63 | } | ||
64 | |||
65 | static int if_version(struct cardstate *cs, unsigned arg[4]) | ||
66 | { | ||
67 | static const unsigned version[4] = GIG_VERSION; | ||
68 | static const unsigned compat[4] = GIG_COMPAT; | ||
69 | unsigned cmd = arg[0]; | ||
70 | |||
71 | dbg(DEBUG_IF, "%u: if_version (%d)", cs->minor_index, cmd); | ||
72 | |||
73 | switch (cmd) { | ||
74 | case GIGVER_DRIVER: | ||
75 | memcpy(arg, version, sizeof version); | ||
76 | return 0; | ||
77 | case GIGVER_COMPAT: | ||
78 | memcpy(arg, compat, sizeof compat); | ||
79 | return 0; | ||
80 | case GIGVER_FWBASE: | ||
81 | cs->waiting = 1; | ||
82 | if (!gigaset_add_event(cs, &cs->at_state, EV_IF_VER, | ||
83 | NULL, 0, arg)) { | ||
84 | cs->waiting = 0; | ||
85 | return -ENOMEM; | ||
86 | } | ||
87 | |||
88 | dbg(DEBUG_CMD, "scheduling IF_VER"); | ||
89 | gigaset_schedule_event(cs); | ||
90 | |||
91 | wait_event(cs->waitqueue, !cs->waiting); | ||
92 | |||
93 | if (cs->cmd_result >= 0) | ||
94 | return 0; | ||
95 | |||
96 | return cs->cmd_result; | ||
97 | default: | ||
98 | return -EINVAL; | ||
99 | } | ||
100 | } | ||
101 | |||
102 | static int if_config(struct cardstate *cs, int *arg) | ||
103 | { | ||
104 | dbg(DEBUG_IF, "%u: if_config (%d)", cs->minor_index, *arg); | ||
105 | |||
106 | if (*arg != 1) | ||
107 | return -EINVAL; | ||
108 | |||
109 | if (atomic_read(&cs->mstate) != MS_LOCKED) | ||
110 | return -EBUSY; | ||
111 | |||
112 | *arg = 0; | ||
113 | return gigaset_enterconfigmode(cs); | ||
114 | } | ||
115 | |||
116 | /*** the terminal driver ***/ | ||
117 | /* stolen from usbserial and some other tty drivers */ | ||
118 | |||
119 | static int if_open(struct tty_struct *tty, struct file *filp); | ||
120 | static void if_close(struct tty_struct *tty, struct file *filp); | ||
121 | static int if_ioctl(struct tty_struct *tty, struct file *file, | ||
122 | unsigned int cmd, unsigned long arg); | ||
123 | static int if_write_room(struct tty_struct *tty); | ||
124 | static int if_chars_in_buffer(struct tty_struct *tty); | ||
125 | static void if_throttle(struct tty_struct *tty); | ||
126 | static void if_unthrottle(struct tty_struct *tty); | ||
127 | static void if_set_termios(struct tty_struct *tty, struct termios *old); | ||
128 | static int if_tiocmget(struct tty_struct *tty, struct file *file); | ||
129 | static int if_tiocmset(struct tty_struct *tty, struct file *file, | ||
130 | unsigned int set, unsigned int clear); | ||
131 | static int if_write(struct tty_struct *tty, | ||
132 | const unsigned char *buf, int count); | ||
133 | |||
134 | static struct tty_operations if_ops = { | ||
135 | .open = if_open, | ||
136 | .close = if_close, | ||
137 | .ioctl = if_ioctl, | ||
138 | .write = if_write, | ||
139 | .write_room = if_write_room, | ||
140 | .chars_in_buffer = if_chars_in_buffer, | ||
141 | .set_termios = if_set_termios, | ||
142 | .throttle = if_throttle, | ||
143 | .unthrottle = if_unthrottle, | ||
144 | #if 0 | ||
145 | .break_ctl = serial_break, | ||
146 | #endif | ||
147 | .tiocmget = if_tiocmget, | ||
148 | .tiocmset = if_tiocmset, | ||
149 | }; | ||
150 | |||
151 | static int if_open(struct tty_struct *tty, struct file *filp) | ||
152 | { | ||
153 | struct cardstate *cs; | ||
154 | unsigned long flags; | ||
155 | |||
156 | dbg(DEBUG_IF, "%d+%d: %s()", tty->driver->minor_start, tty->index, | ||
157 | __FUNCTION__); | ||
158 | |||
159 | tty->driver_data = NULL; | ||
160 | |||
161 | cs = gigaset_get_cs_by_tty(tty); | ||
162 | if (!cs) | ||
163 | return -ENODEV; | ||
164 | |||
165 | if (down_interruptible(&cs->sem)) | ||
166 | return -ERESTARTSYS; // FIXME -EINTR? | ||
167 | tty->driver_data = cs; | ||
168 | |||
169 | ++cs->open_count; | ||
170 | |||
171 | if (cs->open_count == 1) { | ||
172 | spin_lock_irqsave(&cs->lock, flags); | ||
173 | cs->tty = tty; | ||
174 | spin_unlock_irqrestore(&cs->lock, flags); | ||
175 | tty->low_latency = 1; //FIXME test | ||
176 | //FIXME | ||
177 | } | ||
178 | |||
179 | up(&cs->sem); | ||
180 | return 0; | ||
181 | } | ||
182 | |||
183 | static void if_close(struct tty_struct *tty, struct file *filp) | ||
184 | { | ||
185 | struct cardstate *cs; | ||
186 | unsigned long flags; | ||
187 | |||
188 | cs = (struct cardstate *) tty->driver_data; | ||
189 | if (!cs) { | ||
190 | err("cs==NULL in %s", __FUNCTION__); | ||
191 | return; | ||
192 | } | ||
193 | |||
194 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
195 | |||
196 | down(&cs->sem); | ||
197 | |||
198 | if (!cs->open_count) | ||
199 | warn("%s: device not opened", __FUNCTION__); | ||
200 | else { | ||
201 | if (!--cs->open_count) { | ||
202 | spin_lock_irqsave(&cs->lock, flags); | ||
203 | cs->tty = NULL; | ||
204 | spin_unlock_irqrestore(&cs->lock, flags); | ||
205 | //FIXME | ||
206 | } | ||
207 | } | ||
208 | |||
209 | up(&cs->sem); | ||
210 | } | ||
211 | |||
212 | static int if_ioctl(struct tty_struct *tty, struct file *file, | ||
213 | unsigned int cmd, unsigned long arg) | ||
214 | { | ||
215 | struct cardstate *cs; | ||
216 | int retval = -ENODEV; | ||
217 | int int_arg; | ||
218 | unsigned char buf[6]; | ||
219 | unsigned version[4]; | ||
220 | |||
221 | cs = (struct cardstate *) tty->driver_data; | ||
222 | if (!cs) { | ||
223 | err("cs==NULL in %s", __FUNCTION__); | ||
224 | return -ENODEV; | ||
225 | } | ||
226 | |||
227 | dbg(DEBUG_IF, "%u: %s(0x%x)", cs->minor_index, __FUNCTION__, cmd); | ||
228 | |||
229 | if (down_interruptible(&cs->sem)) | ||
230 | return -ERESTARTSYS; // FIXME -EINTR? | ||
231 | |||
232 | if (!cs->open_count) | ||
233 | warn("%s: device not opened", __FUNCTION__); | ||
234 | else { | ||
235 | retval = 0; | ||
236 | switch (cmd) { | ||
237 | case GIGASET_REDIR: | ||
238 | retval = get_user(int_arg, (int __user *) arg); | ||
239 | if (retval >= 0) | ||
240 | retval = if_lock(cs, &int_arg); | ||
241 | if (retval >= 0) | ||
242 | retval = put_user(int_arg, (int __user *) arg); | ||
243 | break; | ||
244 | case GIGASET_CONFIG: | ||
245 | retval = get_user(int_arg, (int __user *) arg); | ||
246 | if (retval >= 0) | ||
247 | retval = if_config(cs, &int_arg); | ||
248 | if (retval >= 0) | ||
249 | retval = put_user(int_arg, (int __user *) arg); | ||
250 | break; | ||
251 | case GIGASET_BRKCHARS: | ||
252 | //FIXME test if MS_LOCKED | ||
253 | gigaset_dbg_buffer(DEBUG_IF, "GIGASET_BRKCHARS", | ||
254 | 6, (const unsigned char *) arg, 1); | ||
255 | if (!atomic_read(&cs->connected)) { | ||
256 | dbg(DEBUG_ANY, "can't communicate with unplugged device"); | ||
257 | retval = -ENODEV; | ||
258 | break; | ||
259 | } | ||
260 | retval = copy_from_user(&buf, | ||
261 | (const unsigned char __user *) arg, 6) | ||
262 | ? -EFAULT : 0; | ||
263 | if (retval >= 0) | ||
264 | retval = cs->ops->brkchars(cs, buf); | ||
265 | break; | ||
266 | case GIGASET_VERSION: | ||
267 | retval = copy_from_user(version, (unsigned __user *) arg, | ||
268 | sizeof version) ? -EFAULT : 0; | ||
269 | if (retval >= 0) | ||
270 | retval = if_version(cs, version); | ||
271 | if (retval >= 0) | ||
272 | retval = copy_to_user((unsigned __user *) arg, version, | ||
273 | sizeof version) | ||
274 | ? -EFAULT : 0; | ||
275 | break; | ||
276 | default: | ||
277 | dbg(DEBUG_ANY, "%s: arg not supported - 0x%04x", | ||
278 | __FUNCTION__, cmd); | ||
279 | retval = -ENOIOCTLCMD; | ||
280 | } | ||
281 | } | ||
282 | |||
283 | up(&cs->sem); | ||
284 | |||
285 | return retval; | ||
286 | } | ||
287 | |||
288 | static int if_tiocmget(struct tty_struct *tty, struct file *file) | ||
289 | { | ||
290 | struct cardstate *cs; | ||
291 | int retval; | ||
292 | |||
293 | cs = (struct cardstate *) tty->driver_data; | ||
294 | if (!cs) { | ||
295 | err("cs==NULL in %s", __FUNCTION__); | ||
296 | return -ENODEV; | ||
297 | } | ||
298 | |||
299 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
300 | |||
301 | if (down_interruptible(&cs->sem)) | ||
302 | return -ERESTARTSYS; // FIXME -EINTR? | ||
303 | |||
304 | // FIXME read from device? | ||
305 | retval = cs->control_state & (TIOCM_RTS|TIOCM_DTR); | ||
306 | |||
307 | up(&cs->sem); | ||
308 | |||
309 | return retval; | ||
310 | } | ||
311 | |||
312 | static int if_tiocmset(struct tty_struct *tty, struct file *file, | ||
313 | unsigned int set, unsigned int clear) | ||
314 | { | ||
315 | struct cardstate *cs; | ||
316 | int retval; | ||
317 | unsigned mc; | ||
318 | |||
319 | cs = (struct cardstate *) tty->driver_data; | ||
320 | if (!cs) { | ||
321 | err("cs==NULL in %s", __FUNCTION__); | ||
322 | return -ENODEV; | ||
323 | } | ||
324 | |||
325 | dbg(DEBUG_IF, | ||
326 | "%u: %s(0x%x, 0x%x)", cs->minor_index, __FUNCTION__, set, clear); | ||
327 | |||
328 | if (down_interruptible(&cs->sem)) | ||
329 | return -ERESTARTSYS; // FIXME -EINTR? | ||
330 | |||
331 | if (!atomic_read(&cs->connected)) { | ||
332 | dbg(DEBUG_ANY, "can't communicate with unplugged device"); | ||
333 | retval = -ENODEV; | ||
334 | } else { | ||
335 | mc = (cs->control_state | set) & ~clear & (TIOCM_RTS|TIOCM_DTR); | ||
336 | retval = cs->ops->set_modem_ctrl(cs, cs->control_state, mc); | ||
337 | cs->control_state = mc; | ||
338 | } | ||
339 | |||
340 | up(&cs->sem); | ||
341 | |||
342 | return retval; | ||
343 | } | ||
344 | |||
345 | static int if_write(struct tty_struct *tty, const unsigned char *buf, int count) | ||
346 | { | ||
347 | struct cardstate *cs; | ||
348 | int retval = -ENODEV; | ||
349 | |||
350 | cs = (struct cardstate *) tty->driver_data; | ||
351 | if (!cs) { | ||
352 | err("cs==NULL in %s", __FUNCTION__); | ||
353 | return -ENODEV; | ||
354 | } | ||
355 | |||
356 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
357 | |||
358 | if (down_interruptible(&cs->sem)) | ||
359 | return -ERESTARTSYS; // FIXME -EINTR? | ||
360 | |||
361 | if (!cs->open_count) | ||
362 | warn("%s: device not opened", __FUNCTION__); | ||
363 | else if (atomic_read(&cs->mstate) != MS_LOCKED) { | ||
364 | warn("can't write to unlocked device"); | ||
365 | retval = -EBUSY; | ||
366 | } else if (!atomic_read(&cs->connected)) { | ||
367 | dbg(DEBUG_ANY, "can't write to unplugged device"); | ||
368 | retval = -EBUSY; //FIXME | ||
369 | } else { | ||
370 | retval = cs->ops->write_cmd(cs, buf, count, | ||
371 | &cs->if_wake_tasklet); | ||
372 | } | ||
373 | |||
374 | up(&cs->sem); | ||
375 | |||
376 | return retval; | ||
377 | } | ||
378 | |||
379 | static int if_write_room(struct tty_struct *tty) | ||
380 | { | ||
381 | struct cardstate *cs; | ||
382 | int retval = -ENODEV; | ||
383 | |||
384 | cs = (struct cardstate *) tty->driver_data; | ||
385 | if (!cs) { | ||
386 | err("cs==NULL in %s", __FUNCTION__); | ||
387 | return -ENODEV; | ||
388 | } | ||
389 | |||
390 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
391 | |||
392 | if (down_interruptible(&cs->sem)) | ||
393 | return -ERESTARTSYS; // FIXME -EINTR? | ||
394 | |||
395 | if (!cs->open_count) | ||
396 | warn("%s: device not opened", __FUNCTION__); | ||
397 | else if (atomic_read(&cs->mstate) != MS_LOCKED) { | ||
398 | warn("can't write to unlocked device"); | ||
399 | retval = -EBUSY; //FIXME | ||
400 | } else if (!atomic_read(&cs->connected)) { | ||
401 | dbg(DEBUG_ANY, "can't write to unplugged device"); | ||
402 | retval = -EBUSY; //FIXME | ||
403 | } else | ||
404 | retval = cs->ops->write_room(cs); | ||
405 | |||
406 | up(&cs->sem); | ||
407 | |||
408 | return retval; | ||
409 | } | ||
410 | |||
411 | static int if_chars_in_buffer(struct tty_struct *tty) | ||
412 | { | ||
413 | struct cardstate *cs; | ||
414 | int retval = -ENODEV; | ||
415 | |||
416 | cs = (struct cardstate *) tty->driver_data; | ||
417 | if (!cs) { | ||
418 | err("cs==NULL in %s", __FUNCTION__); | ||
419 | return -ENODEV; | ||
420 | } | ||
421 | |||
422 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
423 | |||
424 | if (down_interruptible(&cs->sem)) | ||
425 | return -ERESTARTSYS; // FIXME -EINTR? | ||
426 | |||
427 | if (!cs->open_count) | ||
428 | warn("%s: device not opened", __FUNCTION__); | ||
429 | else if (atomic_read(&cs->mstate) != MS_LOCKED) { | ||
430 | warn("can't write to unlocked device"); | ||
431 | retval = -EBUSY; | ||
432 | } else if (!atomic_read(&cs->connected)) { | ||
433 | dbg(DEBUG_ANY, "can't write to unplugged device"); | ||
434 | retval = -EBUSY; //FIXME | ||
435 | } else | ||
436 | retval = cs->ops->chars_in_buffer(cs); | ||
437 | |||
438 | up(&cs->sem); | ||
439 | |||
440 | return retval; | ||
441 | } | ||
442 | |||
443 | static void if_throttle(struct tty_struct *tty) | ||
444 | { | ||
445 | struct cardstate *cs; | ||
446 | |||
447 | cs = (struct cardstate *) tty->driver_data; | ||
448 | if (!cs) { | ||
449 | err("cs==NULL in %s", __FUNCTION__); | ||
450 | return; | ||
451 | } | ||
452 | |||
453 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
454 | |||
455 | down(&cs->sem); | ||
456 | |||
457 | if (!cs->open_count) | ||
458 | warn("%s: device not opened", __FUNCTION__); | ||
459 | else { | ||
460 | //FIXME | ||
461 | } | ||
462 | |||
463 | up(&cs->sem); | ||
464 | } | ||
465 | |||
466 | static void if_unthrottle(struct tty_struct *tty) | ||
467 | { | ||
468 | struct cardstate *cs; | ||
469 | |||
470 | cs = (struct cardstate *) tty->driver_data; | ||
471 | if (!cs) { | ||
472 | err("cs==NULL in %s", __FUNCTION__); | ||
473 | return; | ||
474 | } | ||
475 | |||
476 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
477 | |||
478 | down(&cs->sem); | ||
479 | |||
480 | if (!cs->open_count) | ||
481 | warn("%s: device not opened", __FUNCTION__); | ||
482 | else { | ||
483 | //FIXME | ||
484 | } | ||
485 | |||
486 | up(&cs->sem); | ||
487 | } | ||
488 | |||
489 | static void if_set_termios(struct tty_struct *tty, struct termios *old) | ||
490 | { | ||
491 | struct cardstate *cs; | ||
492 | unsigned int iflag; | ||
493 | unsigned int cflag; | ||
494 | unsigned int old_cflag; | ||
495 | unsigned int control_state, new_state; | ||
496 | |||
497 | cs = (struct cardstate *) tty->driver_data; | ||
498 | if (!cs) { | ||
499 | err("cs==NULL in %s", __FUNCTION__); | ||
500 | return; | ||
501 | } | ||
502 | |||
503 | dbg(DEBUG_IF, "%u: %s()", cs->minor_index, __FUNCTION__); | ||
504 | |||
505 | down(&cs->sem); | ||
506 | |||
507 | if (!cs->open_count) { | ||
508 | warn("%s: device not opened", __FUNCTION__); | ||
509 | goto out; | ||
510 | } | ||
511 | |||
512 | if (!atomic_read(&cs->connected)) { | ||
513 | dbg(DEBUG_ANY, "can't communicate with unplugged device"); | ||
514 | goto out; | ||
515 | } | ||
516 | |||
517 | // stolen from mct_u232.c | ||
518 | iflag = tty->termios->c_iflag; | ||
519 | cflag = tty->termios->c_cflag; | ||
520 | old_cflag = old ? old->c_cflag : cflag; //FIXME? | ||
521 | dbg(DEBUG_IF, "%u: iflag %x cflag %x old %x", cs->minor_index, | ||
522 | iflag, cflag, old_cflag); | ||
523 | |||
524 | /* get a local copy of the current port settings */ | ||
525 | control_state = cs->control_state; | ||
526 | |||
527 | /* | ||
528 | * Update baud rate. | ||
529 | * Do not attempt to cache old rates and skip settings, | ||
530 | * disconnects screw such tricks up completely. | ||
531 | * Premature optimization is the root of all evil. | ||
532 | */ | ||
533 | |||
534 | /* reassert DTR and (maybe) RTS on transition from B0 */ | ||
535 | if ((old_cflag & CBAUD) == B0) { | ||
536 | new_state = control_state | TIOCM_DTR; | ||
537 | /* don't set RTS if using hardware flow control */ | ||
538 | if (!(old_cflag & CRTSCTS)) | ||
539 | new_state |= TIOCM_RTS; | ||
540 | dbg(DEBUG_IF, "%u: from B0 - set DTR%s", cs->minor_index, | ||
541 | (new_state & TIOCM_RTS) ? " only" : "/RTS"); | ||
542 | cs->ops->set_modem_ctrl(cs, control_state, new_state); | ||
543 | control_state = new_state; | ||
544 | } | ||
545 | |||
546 | cs->ops->baud_rate(cs, cflag & CBAUD); | ||
547 | |||
548 | if ((cflag & CBAUD) == B0) { | ||
549 | /* Drop RTS and DTR */ | ||
550 | dbg(DEBUG_IF, "%u: to B0 - drop DTR/RTS", cs->minor_index); | ||
551 | new_state = control_state & ~(TIOCM_DTR | TIOCM_RTS); | ||
552 | cs->ops->set_modem_ctrl(cs, control_state, new_state); | ||
553 | control_state = new_state; | ||
554 | } | ||
555 | |||
556 | /* | ||
557 | * Update line control register (LCR) | ||
558 | */ | ||
559 | |||
560 | cs->ops->set_line_ctrl(cs, cflag); | ||
561 | |||
562 | #if 0 | ||
563 | //FIXME this hangs M101 [ts 2005-03-09] | ||
564 | //FIXME do we need this? | ||
565 | /* | ||
566 | * Set flow control: well, I do not really now how to handle DTR/RTS. | ||
567 | * Just do what we have seen with SniffUSB on Win98. | ||
568 | */ | ||
569 | /* Drop DTR/RTS if no flow control otherwise assert */ | ||
570 | dbg(DEBUG_IF, "%u: control_state %x", cs->minor_index, control_state); | ||
571 | new_state = control_state; | ||
572 | if ((iflag & IXOFF) || (iflag & IXON) || (cflag & CRTSCTS)) | ||
573 | new_state |= TIOCM_DTR | TIOCM_RTS; | ||
574 | else | ||
575 | new_state &= ~(TIOCM_DTR | TIOCM_RTS); | ||
576 | if (new_state != control_state) { | ||
577 | dbg(DEBUG_IF, "%u: new_state %x", cs->minor_index, new_state); | ||
578 | gigaset_set_modem_ctrl(cs, control_state, new_state); // FIXME: mct_u232.c sets the old state here. is this a bug? | ||
579 | control_state = new_state; | ||
580 | } | ||
581 | #endif | ||
582 | |||
583 | /* save off the modified port settings */ | ||
584 | cs->control_state = control_state; | ||
585 | |||
586 | out: | ||
587 | up(&cs->sem); | ||
588 | } | ||
589 | |||
590 | |||
591 | /* wakeup tasklet for the write operation */ | ||
592 | static void if_wake(unsigned long data) | ||
593 | { | ||
594 | struct cardstate *cs = (struct cardstate *) data; | ||
595 | struct tty_struct *tty; | ||
596 | |||
597 | tty = cs->tty; | ||
598 | if (!tty) | ||
599 | return; | ||
600 | |||
601 | if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && | ||
602 | tty->ldisc.write_wakeup) { | ||
603 | dbg(DEBUG_IF, "write wakeup call"); | ||
604 | tty->ldisc.write_wakeup(tty); | ||
605 | } | ||
606 | |||
607 | wake_up_interruptible(&tty->write_wait); | ||
608 | } | ||
609 | |||
610 | /*** interface to common ***/ | ||
611 | |||
612 | void gigaset_if_init(struct cardstate *cs) | ||
613 | { | ||
614 | struct gigaset_driver *drv; | ||
615 | |||
616 | drv = cs->driver; | ||
617 | if (!drv->have_tty) | ||
618 | return; | ||
619 | |||
620 | tasklet_init(&cs->if_wake_tasklet, &if_wake, (unsigned long) cs); | ||
621 | tty_register_device(drv->tty, cs->minor_index, NULL); | ||
622 | } | ||
623 | |||
624 | void gigaset_if_free(struct cardstate *cs) | ||
625 | { | ||
626 | struct gigaset_driver *drv; | ||
627 | |||
628 | drv = cs->driver; | ||
629 | if (!drv->have_tty) | ||
630 | return; | ||
631 | |||
632 | tasklet_disable(&cs->if_wake_tasklet); | ||
633 | tasklet_kill(&cs->if_wake_tasklet); | ||
634 | tty_unregister_device(drv->tty, cs->minor_index); | ||
635 | } | ||
636 | |||
637 | void gigaset_if_receive(struct cardstate *cs, | ||
638 | unsigned char *buffer, size_t len) | ||
639 | { | ||
640 | unsigned long flags; | ||
641 | struct tty_struct *tty; | ||
642 | |||
643 | spin_lock_irqsave(&cs->lock, flags); | ||
644 | if ((tty = cs->tty) == NULL) | ||
645 | dbg(DEBUG_ANY, "receive on closed device"); | ||
646 | else { | ||
647 | tty_buffer_request_room(tty, len); | ||
648 | tty_insert_flip_string(tty, buffer, len); | ||
649 | tty_flip_buffer_push(tty); | ||
650 | } | ||
651 | spin_unlock_irqrestore(&cs->lock, flags); | ||
652 | } | ||
653 | EXPORT_SYMBOL_GPL(gigaset_if_receive); | ||
654 | |||
655 | /* gigaset_if_initdriver | ||
656 | * Initialize tty interface. | ||
657 | * parameters: | ||
658 | * drv Driver | ||
659 | * procname Name of the driver (e.g. for /proc/tty/drivers) | ||
660 | * devname Name of the device files (prefix without minor number) | ||
661 | * devfsname Devfs name of the device files without %d | ||
662 | */ | ||
663 | void gigaset_if_initdriver(struct gigaset_driver *drv, const char *procname, | ||
664 | const char *devname, const char *devfsname) | ||
665 | { | ||
666 | unsigned minors = drv->minors; | ||
667 | int ret; | ||
668 | struct tty_driver *tty; | ||
669 | |||
670 | drv->have_tty = 0; | ||
671 | |||
672 | if ((drv->tty = alloc_tty_driver(minors)) == NULL) | ||
673 | goto enomem; | ||
674 | tty = drv->tty; | ||
675 | |||
676 | tty->magic = TTY_DRIVER_MAGIC, | ||
677 | tty->major = GIG_MAJOR, | ||
678 | tty->type = TTY_DRIVER_TYPE_SERIAL, | ||
679 | tty->subtype = SERIAL_TYPE_NORMAL, | ||
680 | tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, | ||
681 | |||
682 | tty->driver_name = procname; | ||
683 | tty->name = devname; | ||
684 | tty->minor_start = drv->minor; | ||
685 | tty->num = drv->minors; | ||
686 | |||
687 | tty->owner = THIS_MODULE; | ||
688 | tty->devfs_name = devfsname; | ||
689 | |||
690 | tty->init_termios = tty_std_termios; //FIXME | ||
691 | tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; //FIXME | ||
692 | tty_set_operations(tty, &if_ops); | ||
693 | |||
694 | ret = tty_register_driver(tty); | ||
695 | if (ret < 0) { | ||
696 | warn("failed to register tty driver (error %d)", ret); | ||
697 | goto error; | ||
698 | } | ||
699 | dbg(DEBUG_IF, "tty driver initialized"); | ||
700 | drv->have_tty = 1; | ||
701 | return; | ||
702 | |||
703 | enomem: | ||
704 | warn("could not allocate tty structures"); | ||
705 | error: | ||
706 | if (drv->tty) | ||
707 | put_tty_driver(drv->tty); | ||
708 | } | ||
709 | |||
710 | void gigaset_if_freedriver(struct gigaset_driver *drv) | ||
711 | { | ||
712 | if (!drv->have_tty) | ||
713 | return; | ||
714 | |||
715 | drv->have_tty = 0; | ||
716 | tty_unregister_driver(drv->tty); | ||
717 | put_tty_driver(drv->tty); | ||
718 | } | ||