diff options
| author | Tilman Schmidt <tilman@imap.cc> | 2007-02-12 03:52:34 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-02-12 12:48:30 -0500 |
| commit | 2869b23e4b95cbafffcd2fe110d77aff8c218405 (patch) | |
| tree | 150d1be0ec223f8ab5309f5fdcb487b209441342 | |
| parent | e13df2c58f8e8c72278b61e8f59de9a1403f9426 (diff) | |
[PATCH] drivers/isdn/gigaset: new M101 driver (v2)
This patch adds the line discipline based driver for the Gigaset M101
wireless RS232 adapter. It also improves the documentation a bit.
Signed-off-by: Tilman Schmidt <tilman@imap.cc>
Signed-off-by: Hansjoerg Lipp <hjlipp@web.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | Documentation/isdn/README.gigaset | 65 | ||||
| -rw-r--r-- | drivers/isdn/gigaset/Kconfig | 25 | ||||
| -rw-r--r-- | drivers/isdn/gigaset/Makefile | 2 | ||||
| -rw-r--r-- | drivers/isdn/gigaset/asyncdata.c | 5 | ||||
| -rw-r--r-- | drivers/isdn/gigaset/common.c | 20 | ||||
| -rw-r--r-- | drivers/isdn/gigaset/ev-layer.c | 2 | ||||
| -rw-r--r-- | drivers/isdn/gigaset/ser-gigaset.c | 837 | ||||
| -rw-r--r-- | include/linux/gigaset_dev.h | 2 |
8 files changed, 916 insertions, 42 deletions
diff --git a/Documentation/isdn/README.gigaset b/Documentation/isdn/README.gigaset index fa0d4cca964a..55b2852904a4 100644 --- a/Documentation/isdn/README.gigaset +++ b/Documentation/isdn/README.gigaset | |||
| @@ -8,29 +8,33 @@ GigaSet 307x Device Driver | |||
| 8 | This release supports the connection of the Gigaset 307x/417x family of | 8 | This release supports the connection of the Gigaset 307x/417x family of |
| 9 | ISDN DECT bases via Gigaset M101 Data, Gigaset M105 Data or direct USB | 9 | ISDN DECT bases via Gigaset M101 Data, Gigaset M105 Data or direct USB |
| 10 | connection. The following devices are reported to be compatible: | 10 | connection. The following devices are reported to be compatible: |
| 11 | 307x/417x: | 11 | |
| 12 | Gigaset SX255isdn | 12 | Bases: |
| 13 | Gigaset SX353isdn | 13 | Siemens Gigaset 3070/3075 isdn |
| 14 | Sinus 45 [AB] isdn (Deutsche Telekom) | 14 | Siemens Gigaset 4170/4175 isdn |
| 15 | Sinus 721X/XA | 15 | Siemens Gigaset SX205/255 |
| 16 | Siemens Gigaset SX353 | ||
| 17 | T-Com Sinus 45 [AB] isdn | ||
| 18 | T-Com Sinus 721X[A] [SE] | ||
| 16 | Vox Chicago 390 ISDN (KPN Telecom) | 19 | Vox Chicago 390 ISDN (KPN Telecom) |
| 17 | M101: | 20 | |
| 18 | Sinus 45 Data 1 (Telekom) | 21 | RS232 data boxes: |
| 19 | M105: | 22 | Siemens Gigaset M101 Data |
| 20 | Gigaset USB Adapter DECT | 23 | T-Com Sinus 45 Data 1 |
| 21 | Sinus 45 Data 2 (Telekom) | 24 | |
| 22 | Sinus 721 data | 25 | USB data boxes: |
| 26 | Siemens Gigaset M105 Data | ||
| 27 | Siemens Gigaset USB Adapter DECT | ||
| 28 | T-Com Sinus 45 Data 2 | ||
| 29 | T-Com Sinus 721 data | ||
| 23 | Chicago 390 USB (KPN) | 30 | Chicago 390 USB (KPN) |
| 31 | |||
| 24 | See also http://www.erbze.info/sinus_gigaset.htm and | 32 | See also http://www.erbze.info/sinus_gigaset.htm and |
| 25 | http://gigaset307x.sourceforge.net/ | 33 | http://gigaset307x.sourceforge.net/ |
| 26 | 34 | ||
| 27 | We had also reports from users of Gigaset M105 who could use the drivers | 35 | We had also reports from users of Gigaset M105 who could use the drivers |
| 28 | with SX 100 and CX 100 ISDN bases (only in unimodem mode, see section 2.4.) | 36 | with SX 100 and CX 100 ISDN bases (only in unimodem mode, see section 2.4.) |
| 29 | If you have another device that works with our driver, please let us know. | 37 | If you have another device that works with our driver, please let us know. |
| 30 | For example, Gigaset SX205isdn/Sinus 721 X SE and Gigaset SX303isdn bases | ||
| 31 | are just versions without answering machine of models known to work, so | ||
| 32 | they should work just as well; but so far we are lacking positive reports | ||
| 33 | on these. | ||
| 34 | 38 | ||
| 35 | Chances of getting an USB device to work are good if the output of | 39 | Chances of getting an USB device to work are good if the output of |
| 36 | lsusb | 40 | lsusb |
| @@ -60,14 +64,28 @@ GigaSet 307x Device Driver | |||
| 60 | To get the device working, you have to load the proper kernel module. You | 64 | To get the device working, you have to load the proper kernel module. You |
| 61 | can do this using | 65 | can do this using |
| 62 | modprobe modulename | 66 | modprobe modulename |
| 63 | where modulename is usb_gigaset (M105) or bas_gigaset (direct USB | 67 | where modulename is ser_gigaset (M101), usb_gigaset (M105), or |
| 64 | connection to the base). | 68 | bas_gigaset (direct USB connection to the base). |
| 69 | |||
| 70 | The module ser_gigaset provides a serial line discipline N_GIGASET_M101 | ||
| 71 | which drives the device through the regular serial line driver. To use it, | ||
| 72 | run the Gigaset M101 daemon "gigasetm101d" (also available from | ||
| 73 | http://sourceforge.net/projects/gigaset307x/) with the device file of the | ||
| 74 | RS232 port to the M101 as an argument, for example: | ||
| 75 | gigasetm101d /dev/ttyS1 | ||
| 76 | This will open the device file, set its line discipline to N_GIGASET_M101, | ||
| 77 | and then sleep in the background, keeping the device open so that the | ||
| 78 | line discipline remains active. To deactivate it, kill the daemon, for | ||
| 79 | example with | ||
| 80 | killall gigasetm101d | ||
| 81 | before disconnecting the device. | ||
| 65 | 82 | ||
| 66 | 2.2. Device nodes for user space programs | 83 | 2.2. Device nodes for user space programs |
| 67 | ------------------------------------ | 84 | ------------------------------------ |
| 68 | The device can be accessed from user space (eg. by the user space tools | 85 | The device can be accessed from user space (eg. by the user space tools |
| 69 | mentioned in 1.2.) through the device nodes: | 86 | mentioned in 1.2.) through the device nodes: |
| 70 | 87 | ||
| 88 | - /dev/ttyGS0 for M101 (RS232 data boxes) | ||
| 71 | - /dev/ttyGU0 for M105 (USB data boxes) | 89 | - /dev/ttyGU0 for M105 (USB data boxes) |
| 72 | - /dev/ttyGB0 for the base driver (direct USB connection) | 90 | - /dev/ttyGB0 for the base driver (direct USB connection) |
| 73 | 91 | ||
| @@ -168,6 +186,19 @@ GigaSet 307x Device Driver | |||
| 168 | You can also use /sys/class/tty/ttyGxy/cidmode for changing the CID mode | 186 | You can also use /sys/class/tty/ttyGxy/cidmode for changing the CID mode |
| 169 | setting (ttyGxy is ttyGU0 or ttyGB0). | 187 | setting (ttyGxy is ttyGU0 or ttyGB0). |
| 170 | 188 | ||
| 189 | 2.6. M105 Undocumented USB Requests | ||
| 190 | ------------------------------ | ||
| 191 | |||
| 192 | The Gigaset M105 USB data box understands a couple of useful, but | ||
| 193 | undocumented USB commands. These requests are not used in normal | ||
| 194 | operation (for wireless access to the base), but are needed for access | ||
| 195 | to the M105's own configuration mode (registration to the base, baudrate | ||
| 196 | and line format settings, device status queries) via the gigacontr | ||
| 197 | utility. Their use is disabled in the driver by default for safety | ||
| 198 | reasons but can be enabled by setting the kernel configuration option | ||
| 199 | "Support for undocumented USB requests" (GIGASET_UNDOCREQ) to "Y" and | ||
| 200 | recompiling. | ||
| 201 | |||
| 171 | 202 | ||
| 172 | 3. Troubleshooting | 203 | 3. Troubleshooting |
| 173 | --------------- | 204 | --------------- |
diff --git a/drivers/isdn/gigaset/Kconfig b/drivers/isdn/gigaset/Kconfig index 708d47a6484b..bcbb6502a773 100644 --- a/drivers/isdn/gigaset/Kconfig +++ b/drivers/isdn/gigaset/Kconfig | |||
| @@ -7,7 +7,13 @@ config ISDN_DRV_GIGASET | |||
| 7 | select CRC_CCITT | 7 | select CRC_CCITT |
| 8 | select BITREVERSE | 8 | select BITREVERSE |
| 9 | help | 9 | help |
| 10 | Say m here if you have a Gigaset or Sinus isdn device. | 10 | This driver supports the Siemens Gigaset SX205/255 family of |
| 11 | ISDN DECT bases, including the predecessors Gigaset 3070/3075 | ||
| 12 | and 4170/4175 and their T-Com versions Sinus 45isdn and Sinus | ||
| 13 | 721X. | ||
| 14 | If you have one of these devices, say M here and for at least | ||
| 15 | one of the connection specific parts that follow. | ||
| 16 | This will build a module called "gigaset". | ||
| 11 | 17 | ||
| 12 | if ISDN_DRV_GIGASET!=n | 18 | if ISDN_DRV_GIGASET!=n |
| 13 | 19 | ||
| @@ -15,14 +21,25 @@ config GIGASET_BASE | |||
| 15 | tristate "Gigaset base station support" | 21 | tristate "Gigaset base station support" |
| 16 | depends on ISDN_DRV_GIGASET && USB | 22 | depends on ISDN_DRV_GIGASET && USB |
| 17 | help | 23 | help |
| 18 | Say m here if you need to communicate with the base | 24 | Say M here if you want to use the USB interface of the Gigaset |
| 19 | directly via USB. | 25 | base for connection to your system. |
| 26 | This will build a module called "bas_gigaset". | ||
| 20 | 27 | ||
| 21 | config GIGASET_M105 | 28 | config GIGASET_M105 |
| 22 | tristate "Gigaset M105 support" | 29 | tristate "Gigaset M105 support" |
| 23 | depends on ISDN_DRV_GIGASET && USB | 30 | depends on ISDN_DRV_GIGASET && USB |
| 24 | help | 31 | help |
| 25 | Say m here if you need the driver for the Gigaset M105 device. | 32 | Say M here if you want to connect to the Gigaset base via DECT |
| 33 | using a Gigaset M105 (Sinus 45 Data 2) USB DECT device. | ||
| 34 | This will build a module called "usb_gigaset". | ||
| 35 | |||
| 36 | config GIGASET_M101 | ||
| 37 | tristate "Gigaset M101 support" | ||
| 38 | depends on ISDN_DRV_GIGASET | ||
| 39 | help | ||
| 40 | Say M here if you want to connect to the Gigaset base via DECT | ||
| 41 | using a Gigaset M101 (Sinus 45 Data 1) RS232 DECT device. | ||
| 42 | This will build a module called "ser_gigaset". | ||
| 26 | 43 | ||
| 27 | config GIGASET_DEBUG | 44 | config GIGASET_DEBUG |
| 28 | bool "Gigaset debugging" | 45 | bool "Gigaset debugging" |
diff --git a/drivers/isdn/gigaset/Makefile b/drivers/isdn/gigaset/Makefile index 9b9acf1a21ad..835b806a9de7 100644 --- a/drivers/isdn/gigaset/Makefile +++ b/drivers/isdn/gigaset/Makefile | |||
| @@ -1,6 +1,8 @@ | |||
| 1 | gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o | 1 | gigaset-y := common.o interface.o proc.o ev-layer.o i4l.o |
| 2 | usb_gigaset-y := usb-gigaset.o asyncdata.o | 2 | usb_gigaset-y := usb-gigaset.o asyncdata.o |
| 3 | bas_gigaset-y := bas-gigaset.o isocdata.o | 3 | bas_gigaset-y := bas-gigaset.o isocdata.o |
| 4 | ser_gigaset-y := ser-gigaset.o asyncdata.o | ||
| 4 | 5 | ||
| 5 | obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o gigaset.o | 6 | obj-$(CONFIG_GIGASET_M105) += usb_gigaset.o gigaset.o |
| 6 | obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o gigaset.o | 7 | obj-$(CONFIG_GIGASET_BASE) += bas_gigaset.o gigaset.o |
| 8 | obj-$(CONFIG_GIGASET_M105) += ser_gigaset.o gigaset.o | ||
diff --git a/drivers/isdn/gigaset/asyncdata.c b/drivers/isdn/gigaset/asyncdata.c index 88e958f176d2..ddf5e92be444 100644 --- a/drivers/isdn/gigaset/asyncdata.c +++ b/drivers/isdn/gigaset/asyncdata.c | |||
| @@ -13,6 +13,11 @@ | |||
| 13 | * ===================================================================== | 13 | * ===================================================================== |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | /* not set by Kbuild when building both ser_gigaset and usb_gigaset */ | ||
| 17 | #ifndef KBUILD_MODNAME | ||
| 18 | #define KBUILD_MODNAME "asy_gigaset" | ||
| 19 | #endif | ||
| 20 | |||
| 16 | #include "gigaset.h" | 21 | #include "gigaset.h" |
| 17 | #include <linux/crc-ccitt.h> | 22 | #include <linux/crc-ccitt.h> |
| 18 | #include <linux/bitrev.h> | 23 | #include <linux/bitrev.h> |
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c index 9d4ae04eb33f..b460a73a7c85 100644 --- a/drivers/isdn/gigaset/common.c +++ b/drivers/isdn/gigaset/common.c | |||
| @@ -906,20 +906,7 @@ void gigaset_shutdown(struct cardstate *cs) | |||
| 906 | gig_dbg(DEBUG_CMD, "scheduling SHUTDOWN"); | 906 | gig_dbg(DEBUG_CMD, "scheduling SHUTDOWN"); |
| 907 | gigaset_schedule_event(cs); | 907 | gigaset_schedule_event(cs); |
| 908 | 908 | ||
| 909 | if (wait_event_interruptible(cs->waitqueue, !cs->waiting)) { | 909 | wait_event(cs->waitqueue, !cs->waiting); |
| 910 | warn("%s: aborted", __func__); | ||
| 911 | //FIXME | ||
| 912 | } | ||
| 913 | |||
| 914 | if (atomic_read(&cs->mstate) != MS_LOCKED) { | ||
| 915 | //FIXME? | ||
| 916 | //gigaset_baud_rate(cs, B115200); | ||
| 917 | //gigaset_set_line_ctrl(cs, CS8); | ||
| 918 | //gigaset_set_modem_ctrl(cs, TIOCM_DTR|TIOCM_RTS, 0); | ||
| 919 | //cs->control_state = 0; | ||
| 920 | } else { | ||
| 921 | //FIXME use some saved values? | ||
| 922 | } | ||
| 923 | 910 | ||
| 924 | cleanup_cs(cs); | 911 | cleanup_cs(cs); |
| 925 | 912 | ||
| @@ -942,10 +929,7 @@ void gigaset_stop(struct cardstate *cs) | |||
| 942 | gig_dbg(DEBUG_CMD, "scheduling STOP"); | 929 | gig_dbg(DEBUG_CMD, "scheduling STOP"); |
| 943 | gigaset_schedule_event(cs); | 930 | gigaset_schedule_event(cs); |
| 944 | 931 | ||
| 945 | if (wait_event_interruptible(cs->waitqueue, !cs->waiting)) { | 932 | wait_event(cs->waitqueue, !cs->waiting); |
| 946 | warn("%s: aborted", __func__); | ||
| 947 | //FIXME | ||
| 948 | } | ||
| 949 | 933 | ||
| 950 | cleanup_cs(cs); | 934 | cleanup_cs(cs); |
| 951 | 935 | ||
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c index 44f02dbd1111..4661e2c722bc 100644 --- a/drivers/isdn/gigaset/ev-layer.c +++ b/drivers/isdn/gigaset/ev-layer.c | |||
| @@ -1015,7 +1015,7 @@ static void finish_shutdown(struct cardstate *cs) | |||
| 1015 | 1015 | ||
| 1016 | cs->cmd_result = -ENODEV; | 1016 | cs->cmd_result = -ENODEV; |
| 1017 | cs->waiting = 0; | 1017 | cs->waiting = 0; |
| 1018 | wake_up_interruptible(&cs->waitqueue); | 1018 | wake_up(&cs->waitqueue); |
| 1019 | } | 1019 | } |
| 1020 | 1020 | ||
| 1021 | static void do_shutdown(struct cardstate *cs) | 1021 | static void do_shutdown(struct cardstate *cs) |
diff --git a/drivers/isdn/gigaset/ser-gigaset.c b/drivers/isdn/gigaset/ser-gigaset.c new file mode 100644 index 000000000000..c8b7db65e48f --- /dev/null +++ b/drivers/isdn/gigaset/ser-gigaset.c | |||
| @@ -0,0 +1,837 @@ | |||
| 1 | /* This is the serial hardware link layer (HLL) for the Gigaset 307x isdn | ||
| 2 | * DECT base (aka Sinus 45 isdn) using the RS232 DECT data module M101, | ||
| 3 | * written as a line discipline. | ||
| 4 | * | ||
| 5 | * ===================================================================== | ||
| 6 | * This program is free software; you can redistribute it and/or | ||
| 7 | * modify it under the terms of the GNU General Public License as | ||
| 8 | * published by the Free Software Foundation; either version 2 of | ||
| 9 | * the License, or (at your option) any later version. | ||
| 10 | * ===================================================================== | ||
| 11 | */ | ||
| 12 | |||
| 13 | #include "gigaset.h" | ||
| 14 | |||
| 15 | #include <linux/module.h> | ||
| 16 | #include <linux/moduleparam.h> | ||
| 17 | #include <linux/platform_device.h> | ||
| 18 | #include <linux/tty.h> | ||
| 19 | #include <linux/poll.h> | ||
| 20 | |||
| 21 | /* Version Information */ | ||
| 22 | #define DRIVER_AUTHOR "Tilman Schmidt" | ||
| 23 | #define DRIVER_DESC "Serial Driver for Gigaset 307x using Siemens M101" | ||
| 24 | |||
| 25 | #define GIGASET_MINORS 1 | ||
| 26 | #define GIGASET_MINOR 0 | ||
| 27 | #define GIGASET_MODULENAME "ser_gigaset" | ||
| 28 | #define GIGASET_DEVNAME "ttyGS" | ||
| 29 | |||
| 30 | /* length limit according to Siemens 3070usb-protokoll.doc ch. 2.1 */ | ||
| 31 | #define IF_WRITEBUF 264 | ||
| 32 | |||
| 33 | MODULE_AUTHOR(DRIVER_AUTHOR); | ||
| 34 | MODULE_DESCRIPTION(DRIVER_DESC); | ||
| 35 | MODULE_LICENSE("GPL"); | ||
| 36 | MODULE_ALIAS_LDISC(N_GIGASET_M101); | ||
| 37 | |||
| 38 | static int startmode = SM_ISDN; | ||
| 39 | module_param(startmode, int, S_IRUGO); | ||
| 40 | MODULE_PARM_DESC(startmode, "initial operation mode"); | ||
| 41 | static int cidmode = 1; | ||
| 42 | module_param(cidmode, int, S_IRUGO); | ||
| 43 | MODULE_PARM_DESC(cidmode, "stay in CID mode when idle"); | ||
| 44 | |||
| 45 | static struct gigaset_driver *driver; | ||
| 46 | |||
| 47 | struct ser_cardstate { | ||
| 48 | struct platform_device dev; | ||
| 49 | struct tty_struct *tty; | ||
| 50 | atomic_t refcnt; | ||
| 51 | struct mutex dead_mutex; | ||
| 52 | }; | ||
| 53 | |||
| 54 | static struct platform_driver device_driver = { | ||
| 55 | .driver = { | ||
| 56 | .name = GIGASET_MODULENAME, | ||
| 57 | }, | ||
| 58 | }; | ||
| 59 | |||
| 60 | static void flush_send_queue(struct cardstate *); | ||
| 61 | |||
| 62 | /* transmit data from current open skb | ||
| 63 | * result: number of bytes sent or error code < 0 | ||
| 64 | */ | ||
| 65 | static int write_modem(struct cardstate *cs) | ||
| 66 | { | ||
| 67 | struct tty_struct *tty = cs->hw.ser->tty; | ||
| 68 | struct bc_state *bcs = &cs->bcs[0]; /* only one channel */ | ||
| 69 | struct sk_buff *skb = bcs->tx_skb; | ||
| 70 | int sent; | ||
| 71 | |||
| 72 | if (!tty || !tty->driver || !skb) | ||
| 73 | return -EFAULT; | ||
| 74 | |||
| 75 | if (!skb->len) { | ||
| 76 | dev_kfree_skb_any(skb); | ||
| 77 | bcs->tx_skb = NULL; | ||
| 78 | return -EINVAL; | ||
| 79 | } | ||
| 80 | |||
| 81 | set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); | ||
| 82 | sent = tty->driver->write(tty, skb->data, skb->len); | ||
| 83 | gig_dbg(DEBUG_OUTPUT, "write_modem: sent %d", sent); | ||
| 84 | if (sent < 0) { | ||
| 85 | /* error */ | ||
| 86 | flush_send_queue(cs); | ||
| 87 | return sent; | ||
| 88 | } | ||
| 89 | skb_pull(skb, sent); | ||
| 90 | if (!skb->len) { | ||
| 91 | /* skb sent completely */ | ||
| 92 | gigaset_skb_sent(bcs, skb); | ||
| 93 | |||
| 94 | gig_dbg(DEBUG_INTR, "kfree skb (Adr: %lx)!", | ||
| 95 | (unsigned long) skb); | ||
| 96 | dev_kfree_skb_any(skb); | ||
| 97 | bcs->tx_skb = NULL; | ||
| 98 | } | ||
| 99 | return sent; | ||
| 100 | } | ||
| 101 | |||
| 102 | /* | ||
| 103 | * transmit first queued command buffer | ||
| 104 | * result: number of bytes sent or error code < 0 | ||
| 105 | */ | ||
| 106 | static int send_cb(struct cardstate *cs) | ||
| 107 | { | ||
| 108 | struct tty_struct *tty = cs->hw.ser->tty; | ||
| 109 | struct cmdbuf_t *cb, *tcb; | ||
| 110 | unsigned long flags; | ||
| 111 | int sent = 0; | ||
| 112 | |||
| 113 | if (!tty || !tty->driver) | ||
| 114 | return -EFAULT; | ||
| 115 | |||
| 116 | cb = cs->cmdbuf; | ||
| 117 | if (!cb) | ||
| 118 | return 0; /* nothing to do */ | ||
| 119 | |||
| 120 | if (cb->len) { | ||
| 121 | set_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); | ||
| 122 | sent = tty->driver->write(tty, cb->buf + cb->offset, cb->len); | ||
| 123 | if (sent < 0) { | ||
| 124 | /* error */ | ||
| 125 | gig_dbg(DEBUG_OUTPUT, "send_cb: write error %d", sent); | ||
| 126 | flush_send_queue(cs); | ||
| 127 | return sent; | ||
| 128 | } | ||
| 129 | cb->offset += sent; | ||
| 130 | cb->len -= sent; | ||
| 131 | gig_dbg(DEBUG_OUTPUT, "send_cb: sent %d, left %u, queued %u", | ||
| 132 | sent, cb->len, cs->cmdbytes); | ||
| 133 | } | ||
| 134 | |||
| 135 | while (cb && !cb->len) { | ||
| 136 | spin_lock_irqsave(&cs->cmdlock, flags); | ||
| 137 | cs->cmdbytes -= cs->curlen; | ||
| 138 | tcb = cb; | ||
| 139 | cs->cmdbuf = cb = cb->next; | ||
| 140 | if (cb) { | ||
| 141 | cb->prev = NULL; | ||
| 142 | cs->curlen = cb->len; | ||
| 143 | } else { | ||
| 144 | cs->lastcmdbuf = NULL; | ||
| 145 | cs->curlen = 0; | ||
| 146 | } | ||
| 147 | spin_unlock_irqrestore(&cs->cmdlock, flags); | ||
| 148 | |||
| 149 | if (tcb->wake_tasklet) | ||
| 150 | tasklet_schedule(tcb->wake_tasklet); | ||
| 151 | kfree(tcb); | ||
| 152 | } | ||
| 153 | return sent; | ||
| 154 | } | ||
| 155 | |||
| 156 | /* | ||
| 157 | * send queue tasklet | ||
| 158 | * If there is already a skb opened, put data to the transfer buffer | ||
| 159 | * by calling "write_modem". | ||
| 160 | * Otherwise take a new skb out of the queue. | ||
| 161 | */ | ||
| 162 | static void gigaset_modem_fill(unsigned long data) | ||
| 163 | { | ||
| 164 | struct cardstate *cs = (struct cardstate *) data; | ||
| 165 | struct bc_state *bcs; | ||
| 166 | int sent = 0; | ||
| 167 | |||
| 168 | if (!cs || !(bcs = cs->bcs)) { | ||
| 169 | gig_dbg(DEBUG_OUTPUT, "%s: no cardstate", __func__); | ||
| 170 | return; | ||
| 171 | } | ||
| 172 | if (!bcs->tx_skb) { | ||
| 173 | /* no skb is being sent; send command if any */ | ||
| 174 | sent = send_cb(cs); | ||
| 175 | gig_dbg(DEBUG_OUTPUT, "%s: send_cb -> %d", __func__, sent); | ||
| 176 | if (sent) | ||
| 177 | /* something sent or error */ | ||
| 178 | return; | ||
| 179 | |||
| 180 | /* no command to send; get skb */ | ||
| 181 | if (!(bcs->tx_skb = skb_dequeue(&bcs->squeue))) | ||
| 182 | /* no skb either, nothing to do */ | ||
| 183 | return; | ||
| 184 | |||
| 185 | gig_dbg(DEBUG_INTR, "Dequeued skb (Adr: %lx)", | ||
| 186 | (unsigned long) bcs->tx_skb); | ||
| 187 | } | ||
| 188 | |||
| 189 | /* send skb */ | ||
| 190 | gig_dbg(DEBUG_OUTPUT, "%s: tx_skb", __func__); | ||
| 191 | if (write_modem(cs) < 0) | ||
| 192 | gig_dbg(DEBUG_OUTPUT, "%s: write_modem failed", __func__); | ||
| 193 | } | ||
| 194 | |||
| 195 | /* | ||
| 196 | * throw away all data queued for sending | ||
| 197 | */ | ||
| 198 | static void flush_send_queue(struct cardstate *cs) | ||
| 199 | { | ||
| 200 | struct sk_buff *skb; | ||
| 201 | struct cmdbuf_t *cb; | ||
| 202 | unsigned long flags; | ||
| 203 | |||
| 204 | /* command queue */ | ||
| 205 | spin_lock_irqsave(&cs->cmdlock, flags); | ||
| 206 | while ((cb = cs->cmdbuf) != NULL) { | ||
| 207 | cs->cmdbuf = cb->next; | ||
| 208 | if (cb->wake_tasklet) | ||
| 209 | tasklet_schedule(cb->wake_tasklet); | ||
| 210 | kfree(cb); | ||
| 211 | } | ||
| 212 | cs->cmdbuf = cs->lastcmdbuf = NULL; | ||
| 213 | cs->cmdbytes = cs->curlen = 0; | ||
| 214 | spin_unlock_irqrestore(&cs->cmdlock, flags); | ||
| 215 | |||
| 216 | /* data queue */ | ||
| 217 | if (cs->bcs->tx_skb) | ||
| 218 | dev_kfree_skb_any(cs->bcs->tx_skb); | ||
| 219 | while ((skb = skb_dequeue(&cs->bcs->squeue)) != NULL) | ||
| 220 | dev_kfree_skb_any(skb); | ||
| 221 | } | ||
| 222 | |||
| 223 | |||
| 224 | /* Gigaset Driver Interface */ | ||
| 225 | /* ======================== */ | ||
| 226 | |||
| 227 | /* | ||
| 228 | * queue an AT command string for transmission to the Gigaset device | ||
| 229 | * parameters: | ||
| 230 | * cs controller state structure | ||
| 231 | * buf buffer containing the string to send | ||
| 232 | * len number of characters to send | ||
| 233 | * wake_tasklet tasklet to run when transmission is complete, or NULL | ||
| 234 | * return value: | ||
| 235 | * number of bytes queued, or error code < 0 | ||
| 236 | */ | ||
| 237 | static int gigaset_write_cmd(struct cardstate *cs, const unsigned char *buf, | ||
| 238 | int len, struct tasklet_struct *wake_tasklet) | ||
| 239 | { | ||
| 240 | struct cmdbuf_t *cb; | ||
| 241 | unsigned long flags; | ||
| 242 | |||
| 243 | gigaset_dbg_buffer(atomic_read(&cs->mstate) != MS_LOCKED ? | ||
| 244 | DEBUG_TRANSCMD : DEBUG_LOCKCMD, | ||
| 245 | "CMD Transmit", len, buf); | ||
| 246 | |||
| 247 | if (len <= 0) | ||
| 248 | return 0; | ||
| 249 | |||
| 250 | if (!(cb = kmalloc(sizeof(struct cmdbuf_t) + len, GFP_ATOMIC))) { | ||
| 251 | dev_err(cs->dev, "%s: out of memory!\n", __func__); | ||
| 252 | return -ENOMEM; | ||
| 253 | } | ||
| 254 | |||
| 255 | memcpy(cb->buf, buf, len); | ||
| 256 | cb->len = len; | ||
| 257 | cb->offset = 0; | ||
| 258 | cb->next = NULL; | ||
| 259 | cb->wake_tasklet = wake_tasklet; | ||
| 260 | |||
| 261 | spin_lock_irqsave(&cs->cmdlock, flags); | ||
| 262 | cb->prev = cs->lastcmdbuf; | ||
| 263 | if (cs->lastcmdbuf) | ||
| 264 | cs->lastcmdbuf->next = cb; | ||
| 265 | else { | ||
| 266 | cs->cmdbuf = cb; | ||
| 267 | cs->curlen = len; | ||
| 268 | } | ||
| 269 | cs->cmdbytes += len; | ||
| 270 | cs->lastcmdbuf = cb; | ||
| 271 | spin_unlock_irqrestore(&cs->cmdlock, flags); | ||
| 272 | |||
| 273 | spin_lock_irqsave(&cs->lock, flags); | ||
| 274 | if (cs->connected) | ||
| 275 | tasklet_schedule(&cs->write_tasklet); | ||
| 276 | spin_unlock_irqrestore(&cs->lock, flags); | ||
| 277 | return len; | ||
| 278 | } | ||
| 279 | |||
| 280 | /* | ||
| 281 | * tty_driver.write_room interface routine | ||
| 282 | * return number of characters the driver will accept to be written | ||
| 283 | * parameter: | ||
| 284 | * controller state structure | ||
| 285 | * return value: | ||
| 286 | * number of characters | ||
| 287 | */ | ||
| 288 | static int gigaset_write_room(struct cardstate *cs) | ||
| 289 | { | ||
| 290 | unsigned bytes; | ||
| 291 | |||
| 292 | bytes = cs->cmdbytes; | ||
| 293 | return bytes < IF_WRITEBUF ? IF_WRITEBUF - bytes : 0; | ||
| 294 | } | ||
| 295 | |||
| 296 | /* | ||
| 297 | * tty_driver.chars_in_buffer interface routine | ||
| 298 | * return number of characters waiting to be sent | ||
| 299 | * parameter: | ||
| 300 | * controller state structure | ||
| 301 | * return value: | ||
| 302 | * number of characters | ||
| 303 | */ | ||
| 304 | static int gigaset_chars_in_buffer(struct cardstate *cs) | ||
| 305 | { | ||
| 306 | return cs->cmdbytes; | ||
| 307 | } | ||
| 308 | |||
| 309 | /* | ||
| 310 | * implementation of ioctl(GIGASET_BRKCHARS) | ||
| 311 | * parameter: | ||
| 312 | * controller state structure | ||
| 313 | * return value: | ||
| 314 | * -EINVAL (unimplemented function) | ||
| 315 | */ | ||
| 316 | static int gigaset_brkchars(struct cardstate *cs, const unsigned char buf[6]) | ||
| 317 | { | ||
| 318 | /* not implemented */ | ||
| 319 | return -EINVAL; | ||
| 320 | } | ||
| 321 | |||
| 322 | /* | ||
| 323 | * Open B channel | ||
| 324 | * Called by "do_action" in ev-layer.c | ||
| 325 | */ | ||
| 326 | static int gigaset_init_bchannel(struct bc_state *bcs) | ||
| 327 | { | ||
| 328 | /* nothing to do for M10x */ | ||
| 329 | gigaset_bchannel_up(bcs); | ||
| 330 | return 0; | ||
| 331 | } | ||
| 332 | |||
| 333 | /* | ||
| 334 | * Close B channel | ||
| 335 | * Called by "do_action" in ev-layer.c | ||
| 336 | */ | ||
| 337 | static int gigaset_close_bchannel(struct bc_state *bcs) | ||
| 338 | { | ||
| 339 | /* nothing to do for M10x */ | ||
| 340 | gigaset_bchannel_down(bcs); | ||
| 341 | return 0; | ||
| 342 | } | ||
| 343 | |||
| 344 | /* | ||
| 345 | * Set up B channel structure | ||
| 346 | * This is called by "gigaset_initcs" in common.c | ||
| 347 | */ | ||
| 348 | static int gigaset_initbcshw(struct bc_state *bcs) | ||
| 349 | { | ||
| 350 | /* unused */ | ||
| 351 | bcs->hw.ser = NULL; | ||
| 352 | return 1; | ||
| 353 | } | ||
| 354 | |||
| 355 | /* | ||
| 356 | * Free B channel structure | ||
| 357 | * Called by "gigaset_freebcs" in common.c | ||
| 358 | */ | ||
| 359 | static int gigaset_freebcshw(struct bc_state *bcs) | ||
| 360 | { | ||
| 361 | /* unused */ | ||
| 362 | return 1; | ||
| 363 | } | ||
| 364 | |||
| 365 | /* | ||
| 366 | * Reinitialize B channel structure | ||
| 367 | * This is called by "bcs_reinit" in common.c | ||
| 368 | */ | ||
| 369 | static void gigaset_reinitbcshw(struct bc_state *bcs) | ||
| 370 | { | ||
| 371 | /* nothing to do for M10x */ | ||
| 372 | } | ||
| 373 | |||
| 374 | /* | ||
| 375 | * Free hardware specific device data | ||
| 376 | * This will be called by "gigaset_freecs" in common.c | ||
| 377 | */ | ||
| 378 | static void gigaset_freecshw(struct cardstate *cs) | ||
| 379 | { | ||
| 380 | tasklet_kill(&cs->write_tasklet); | ||
| 381 | if (!cs->hw.ser) | ||
| 382 | return; | ||
| 383 | dev_set_drvdata(&cs->hw.ser->dev.dev, NULL); | ||
| 384 | platform_device_unregister(&cs->hw.ser->dev); | ||
| 385 | kfree(cs->hw.ser); | ||
| 386 | cs->hw.ser = NULL; | ||
| 387 | } | ||
| 388 | |||
| 389 | static void gigaset_device_release(struct device *dev) | ||
| 390 | { | ||
| 391 | struct platform_device *pdev = | ||
| 392 | container_of(dev, struct platform_device, dev); | ||
| 393 | |||
| 394 | /* adapted from platform_device_release() in drivers/base/platform.c */ | ||
| 395 | //FIXME is this actually necessary? | ||
| 396 | kfree(dev->platform_data); | ||
| 397 | kfree(pdev->resource); | ||
| 398 | } | ||
| 399 | |||
| 400 | /* | ||
| 401 | * Set up hardware specific device data | ||
| 402 | * This is called by "gigaset_initcs" in common.c | ||
| 403 | */ | ||
| 404 | static int gigaset_initcshw(struct cardstate *cs) | ||
| 405 | { | ||
| 406 | int rc; | ||
| 407 | |||
| 408 | if (!(cs->hw.ser = kzalloc(sizeof(struct ser_cardstate), GFP_KERNEL))) { | ||
| 409 | err("%s: out of memory!", __func__); | ||
| 410 | return 0; | ||
| 411 | } | ||
| 412 | |||
| 413 | cs->hw.ser->dev.name = GIGASET_MODULENAME; | ||
| 414 | cs->hw.ser->dev.id = cs->minor_index; | ||
| 415 | cs->hw.ser->dev.dev.release = gigaset_device_release; | ||
| 416 | if ((rc = platform_device_register(&cs->hw.ser->dev)) != 0) { | ||
| 417 | err("error %d registering platform device", rc); | ||
| 418 | kfree(cs->hw.ser); | ||
| 419 | cs->hw.ser = NULL; | ||
| 420 | return 0; | ||
| 421 | } | ||
| 422 | dev_set_drvdata(&cs->hw.ser->dev.dev, cs); | ||
| 423 | |||
| 424 | tasklet_init(&cs->write_tasklet, | ||
| 425 | &gigaset_modem_fill, (unsigned long) cs); | ||
| 426 | return 1; | ||
| 427 | } | ||
| 428 | |||
| 429 | /* | ||
| 430 | * set modem control lines | ||
| 431 | * Parameters: | ||
| 432 | * card state structure | ||
| 433 | * modem control line state ([TIOCM_DTR]|[TIOCM_RTS]) | ||
| 434 | * Called by "gigaset_start" and "gigaset_enterconfigmode" in common.c | ||
| 435 | * and by "if_lock" and "if_termios" in interface.c | ||
| 436 | */ | ||
| 437 | static int gigaset_set_modem_ctrl(struct cardstate *cs, unsigned old_state, unsigned new_state) | ||
| 438 | { | ||
| 439 | struct tty_struct *tty = cs->hw.ser->tty; | ||
| 440 | unsigned int set, clear; | ||
| 441 | |||
| 442 | if (!tty || !tty->driver || !tty->driver->tiocmset) | ||
| 443 | return -EFAULT; | ||
| 444 | set = new_state & ~old_state; | ||
| 445 | clear = old_state & ~new_state; | ||
| 446 | if (!set && !clear) | ||
| 447 | return 0; | ||
| 448 | gig_dbg(DEBUG_IF, "tiocmset set %x clear %x", set, clear); | ||
| 449 | return tty->driver->tiocmset(tty, NULL, set, clear); | ||
| 450 | } | ||
| 451 | |||
| 452 | static int gigaset_baud_rate(struct cardstate *cs, unsigned cflag) | ||
| 453 | { | ||
| 454 | return -EINVAL; | ||
| 455 | } | ||
| 456 | |||
| 457 | static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag) | ||
| 458 | { | ||
| 459 | return -EINVAL; | ||
| 460 | } | ||
| 461 | |||
| 462 | static struct gigaset_ops ops = { | ||
| 463 | gigaset_write_cmd, | ||
| 464 | gigaset_write_room, | ||
| 465 | gigaset_chars_in_buffer, | ||
| 466 | gigaset_brkchars, | ||
| 467 | gigaset_init_bchannel, | ||
| 468 | gigaset_close_bchannel, | ||
| 469 | gigaset_initbcshw, | ||
| 470 | gigaset_freebcshw, | ||
| 471 | gigaset_reinitbcshw, | ||
| 472 | gigaset_initcshw, | ||
| 473 | gigaset_freecshw, | ||
| 474 | gigaset_set_modem_ctrl, | ||
| 475 | gigaset_baud_rate, | ||
| 476 | gigaset_set_line_ctrl, | ||
| 477 | gigaset_m10x_send_skb, /* asyncdata.c */ | ||
| 478 | gigaset_m10x_input, /* asyncdata.c */ | ||
| 479 | }; | ||
| 480 | |||
| 481 | |||
| 482 | /* Line Discipline Interface */ | ||
| 483 | /* ========================= */ | ||
| 484 | |||
| 485 | /* helper functions for cardstate refcounting */ | ||
| 486 | static struct cardstate *cs_get(struct tty_struct *tty) | ||
| 487 | { | ||
| 488 | struct cardstate *cs = tty->disc_data; | ||
| 489 | |||
| 490 | if (!cs || !cs->hw.ser) { | ||
| 491 | gig_dbg(DEBUG_ANY, "%s: no cardstate", __func__); | ||
| 492 | return NULL; | ||
| 493 | } | ||
| 494 | atomic_inc(&cs->hw.ser->refcnt); | ||
| 495 | return cs; | ||
| 496 | } | ||
| 497 | |||
| 498 | static void cs_put(struct cardstate *cs) | ||
| 499 | { | ||
| 500 | if (atomic_dec_and_test(&cs->hw.ser->refcnt)) | ||
| 501 | mutex_unlock(&cs->hw.ser->dead_mutex); | ||
| 502 | } | ||
| 503 | |||
| 504 | /* | ||
| 505 | * Called by the tty driver when the line discipline is pushed onto the tty. | ||
| 506 | * Called in process context. | ||
| 507 | */ | ||
| 508 | static int | ||
| 509 | gigaset_tty_open(struct tty_struct *tty) | ||
| 510 | { | ||
| 511 | struct cardstate *cs; | ||
| 512 | |||
| 513 | gig_dbg(DEBUG_INIT, "Starting HLL for Gigaset M101"); | ||
| 514 | |||
| 515 | info(DRIVER_AUTHOR); | ||
| 516 | info(DRIVER_DESC); | ||
| 517 | |||
| 518 | if (!driver) { | ||
| 519 | err("%s: no driver structure", __func__); | ||
| 520 | return -ENODEV; | ||
| 521 | } | ||
| 522 | |||
| 523 | /* allocate memory for our device state and intialize it */ | ||
| 524 | if (!(cs = gigaset_initcs(driver, 1, 1, 0, cidmode, | ||
| 525 | GIGASET_MODULENAME))) | ||
| 526 | goto error; | ||
| 527 | |||
| 528 | cs->dev = &cs->hw.ser->dev.dev; | ||
| 529 | cs->hw.ser->tty = tty; | ||
| 530 | mutex_init(&cs->hw.ser->dead_mutex); | ||
| 531 | atomic_set(&cs->hw.ser->refcnt, 1); | ||
| 532 | |||
| 533 | tty->disc_data = cs; | ||
| 534 | |||
| 535 | /* OK.. Initialization of the datastructures and the HW is done.. Now | ||
| 536 | * startup system and notify the LL that we are ready to run | ||
| 537 | */ | ||
| 538 | if (startmode == SM_LOCKED) | ||
| 539 | atomic_set(&cs->mstate, MS_LOCKED); | ||
| 540 | if (!gigaset_start(cs)) { | ||
| 541 | tasklet_kill(&cs->write_tasklet); | ||
| 542 | goto error; | ||
| 543 | } | ||
| 544 | |||
| 545 | gig_dbg(DEBUG_INIT, "Startup of HLL done"); | ||
| 546 | mutex_lock(&cs->hw.ser->dead_mutex); | ||
| 547 | return 0; | ||
| 548 | |||
| 549 | error: | ||
| 550 | gig_dbg(DEBUG_INIT, "Startup of HLL failed"); | ||
| 551 | tty->disc_data = NULL; | ||
| 552 | gigaset_freecs(cs); | ||
| 553 | return -ENODEV; | ||
| 554 | } | ||
| 555 | |||
| 556 | /* | ||
| 557 | * Called by the tty driver when the line discipline is removed. | ||
| 558 | * Called from process context. | ||
| 559 | */ | ||
| 560 | static void | ||
| 561 | gigaset_tty_close(struct tty_struct *tty) | ||
| 562 | { | ||
| 563 | struct cardstate *cs = tty->disc_data; | ||
| 564 | |||
| 565 | gig_dbg(DEBUG_INIT, "Stopping HLL for Gigaset M101"); | ||
| 566 | |||
| 567 | if (!cs) { | ||
| 568 | gig_dbg(DEBUG_INIT, "%s: no cardstate", __func__); | ||
| 569 | return; | ||
| 570 | } | ||
| 571 | |||
| 572 | /* prevent other callers from entering ldisc methods */ | ||
| 573 | tty->disc_data = NULL; | ||
| 574 | |||
| 575 | if (!cs->hw.ser) | ||
| 576 | err("%s: no hw cardstate", __func__); | ||
| 577 | else { | ||
| 578 | /* wait for running methods to finish */ | ||
| 579 | if (!atomic_dec_and_test(&cs->hw.ser->refcnt)) | ||
| 580 | mutex_lock(&cs->hw.ser->dead_mutex); | ||
| 581 | } | ||
| 582 | |||
| 583 | /* stop operations */ | ||
| 584 | gigaset_stop(cs); | ||
| 585 | tasklet_kill(&cs->write_tasklet); | ||
| 586 | flush_send_queue(cs); | ||
| 587 | cs->dev = NULL; | ||
| 588 | gigaset_freecs(cs); | ||
| 589 | |||
| 590 | gig_dbg(DEBUG_INIT, "Shutdown of HLL done"); | ||
| 591 | } | ||
| 592 | |||
| 593 | /* | ||
| 594 | * Called by the tty driver when the tty line is hung up. | ||
| 595 | * Wait for I/O to driver to complete and unregister ISDN device. | ||
| 596 | * This is already done by the close routine, so just call that. | ||
| 597 | * Called from process context. | ||
| 598 | */ | ||
| 599 | static int gigaset_tty_hangup(struct tty_struct *tty) | ||
| 600 | { | ||
| 601 | gigaset_tty_close(tty); | ||
| 602 | return 0; | ||
| 603 | } | ||
| 604 | |||
| 605 | /* | ||
| 606 | * Read on the tty. | ||
| 607 | * Unused, received data goes only to the Gigaset driver. | ||
| 608 | */ | ||
| 609 | static ssize_t | ||
| 610 | gigaset_tty_read(struct tty_struct *tty, struct file *file, | ||
| 611 | unsigned char __user *buf, size_t count) | ||
| 612 | { | ||
| 613 | return -EAGAIN; | ||
| 614 | } | ||
| 615 | |||
| 616 | /* | ||
| 617 | * Write on the tty. | ||
| 618 | * Unused, transmit data comes only from the Gigaset driver. | ||
| 619 | */ | ||
| 620 | static ssize_t | ||
| 621 | gigaset_tty_write(struct tty_struct *tty, struct file *file, | ||
| 622 | const unsigned char *buf, size_t count) | ||
| 623 | { | ||
| 624 | return -EAGAIN; | ||
| 625 | } | ||
| 626 | |||
| 627 | /* | ||
| 628 | * Ioctl on the tty. | ||
| 629 | * Called in process context only. | ||
| 630 | * May be re-entered by multiple ioctl calling threads. | ||
| 631 | */ | ||
| 632 | static int | ||
| 633 | gigaset_tty_ioctl(struct tty_struct *tty, struct file *file, | ||
| 634 | unsigned int cmd, unsigned long arg) | ||
| 635 | { | ||
| 636 | struct cardstate *cs = cs_get(tty); | ||
| 637 | int rc, val; | ||
| 638 | int __user *p = (int __user *)arg; | ||
| 639 | |||
| 640 | if (!cs) | ||
| 641 | return -ENXIO; | ||
| 642 | |||
| 643 | switch (cmd) { | ||
| 644 | case TCGETS: | ||
| 645 | case TCGETA: | ||
| 646 | /* pass through to underlying serial device */ | ||
| 647 | rc = n_tty_ioctl(tty, file, cmd, arg); | ||
| 648 | break; | ||
| 649 | |||
| 650 | case TCFLSH: | ||
| 651 | /* flush our buffers and the serial port's buffer */ | ||
| 652 | switch (arg) { | ||
| 653 | case TCIFLUSH: | ||
| 654 | /* no own input buffer to flush */ | ||
| 655 | break; | ||
| 656 | case TCIOFLUSH: | ||
| 657 | case TCOFLUSH: | ||
| 658 | flush_send_queue(cs); | ||
| 659 | break; | ||
| 660 | } | ||
| 661 | /* flush the serial port's buffer */ | ||
| 662 | rc = n_tty_ioctl(tty, file, cmd, arg); | ||
| 663 | break; | ||
| 664 | |||
| 665 | case FIONREAD: | ||
| 666 | /* unused, always return zero */ | ||
| 667 | val = 0; | ||
| 668 | rc = put_user(val, p); | ||
| 669 | break; | ||
| 670 | |||
| 671 | default: | ||
| 672 | rc = -ENOIOCTLCMD; | ||
| 673 | } | ||
| 674 | |||
| 675 | cs_put(cs); | ||
| 676 | return rc; | ||
| 677 | } | ||
| 678 | |||
| 679 | /* | ||
| 680 | * Poll on the tty. | ||
| 681 | * Unused, always return zero. | ||
| 682 | */ | ||
| 683 | static unsigned int | ||
| 684 | gigaset_tty_poll(struct tty_struct *tty, struct file *file, poll_table *wait) | ||
| 685 | { | ||
| 686 | return 0; | ||
| 687 | } | ||
| 688 | |||
| 689 | /* | ||
| 690 | * Called by the tty driver when a block of data has been received. | ||
| 691 | * Will not be re-entered while running but other ldisc functions | ||
| 692 | * may be called in parallel. | ||
| 693 | * Can be called from hard interrupt level as well as soft interrupt | ||
| 694 | * level or mainline. | ||
| 695 | * Parameters: | ||
| 696 | * tty tty structure | ||
| 697 | * buf buffer containing received characters | ||
| 698 | * cflags buffer containing error flags for received characters (ignored) | ||
| 699 | * count number of received characters | ||
| 700 | */ | ||
| 701 | static void | ||
| 702 | gigaset_tty_receive(struct tty_struct *tty, const unsigned char *buf, | ||
| 703 | char *cflags, int count) | ||
| 704 | { | ||
| 705 | struct cardstate *cs = cs_get(tty); | ||
| 706 | unsigned tail, head, n; | ||
| 707 | struct inbuf_t *inbuf; | ||
| 708 | |||
| 709 | if (!cs) | ||
| 710 | return; | ||
| 711 | if (!(inbuf = cs->inbuf)) { | ||
| 712 | dev_err(cs->dev, "%s: no inbuf\n", __func__); | ||
| 713 | cs_put(cs); | ||
| 714 | return; | ||
| 715 | } | ||
| 716 | |||
| 717 | tail = atomic_read(&inbuf->tail); | ||
| 718 | head = atomic_read(&inbuf->head); | ||
| 719 | gig_dbg(DEBUG_INTR, "buffer state: %u -> %u, receive %u bytes", | ||
| 720 | head, tail, count); | ||
| 721 | |||
| 722 | if (head <= tail) { | ||
| 723 | /* possible buffer wraparound */ | ||
| 724 | n = min_t(unsigned, count, RBUFSIZE - tail); | ||
| 725 | memcpy(inbuf->data + tail, buf, n); | ||
| 726 | tail = (tail + n) % RBUFSIZE; | ||
| 727 | buf += n; | ||
| 728 | count -= n; | ||
| 729 | } | ||
| 730 | |||
| 731 | if (count > 0) { | ||
| 732 | /* tail < head and some data left */ | ||
| 733 | n = head - tail - 1; | ||
| 734 | if (count > n) { | ||
| 735 | dev_err(cs->dev, | ||
| 736 | "inbuf overflow, discarding %d bytes\n", | ||
| 737 | count - n); | ||
| 738 | count = n; | ||
| 739 | } | ||
| 740 | memcpy(inbuf->data + tail, buf, count); | ||
| 741 | tail += count; | ||
| 742 | } | ||
| 743 | |||
| 744 | gig_dbg(DEBUG_INTR, "setting tail to %u", tail); | ||
| 745 | atomic_set(&inbuf->tail, tail); | ||
| 746 | |||
| 747 | /* Everything was received .. Push data into handler */ | ||
| 748 | gig_dbg(DEBUG_INTR, "%s-->BH", __func__); | ||
| 749 | gigaset_schedule_event(cs); | ||
| 750 | cs_put(cs); | ||
| 751 | } | ||
| 752 | |||
| 753 | /* | ||
| 754 | * Called by the tty driver when there's room for more data to send. | ||
| 755 | */ | ||
| 756 | static void | ||
| 757 | gigaset_tty_wakeup(struct tty_struct *tty) | ||
| 758 | { | ||
| 759 | struct cardstate *cs = cs_get(tty); | ||
| 760 | |||
| 761 | clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags); | ||
| 762 | if (!cs) | ||
| 763 | return; | ||
| 764 | tasklet_schedule(&cs->write_tasklet); | ||
| 765 | cs_put(cs); | ||
| 766 | } | ||
| 767 | |||
| 768 | static struct tty_ldisc gigaset_ldisc = { | ||
| 769 | .owner = THIS_MODULE, | ||
| 770 | .magic = TTY_LDISC_MAGIC, | ||
| 771 | .name = "ser_gigaset", | ||
| 772 | .open = gigaset_tty_open, | ||
| 773 | .close = gigaset_tty_close, | ||
| 774 | .hangup = gigaset_tty_hangup, | ||
| 775 | .read = gigaset_tty_read, | ||
| 776 | .write = gigaset_tty_write, | ||
| 777 | .ioctl = gigaset_tty_ioctl, | ||
| 778 | .poll = gigaset_tty_poll, | ||
| 779 | .receive_buf = gigaset_tty_receive, | ||
| 780 | .write_wakeup = gigaset_tty_wakeup, | ||
| 781 | }; | ||
| 782 | |||
| 783 | |||
| 784 | /* Initialization / Shutdown */ | ||
| 785 | /* ========================= */ | ||
| 786 | |||
| 787 | static int __init ser_gigaset_init(void) | ||
| 788 | { | ||
| 789 | int rc; | ||
| 790 | |||
| 791 | gig_dbg(DEBUG_INIT, "%s", __func__); | ||
| 792 | if ((rc = platform_driver_register(&device_driver)) != 0) { | ||
| 793 | err("error %d registering platform driver", rc); | ||
| 794 | return rc; | ||
| 795 | } | ||
| 796 | |||
| 797 | /* allocate memory for our driver state and intialize it */ | ||
| 798 | if (!(driver = gigaset_initdriver(GIGASET_MINOR, GIGASET_MINORS, | ||
| 799 | GIGASET_MODULENAME, GIGASET_DEVNAME, | ||
| 800 | &ops, THIS_MODULE))) | ||
| 801 | goto error; | ||
| 802 | |||
| 803 | if ((rc = tty_register_ldisc(N_GIGASET_M101, &gigaset_ldisc)) != 0) { | ||
| 804 | err("error %d registering line discipline", rc); | ||
| 805 | goto error; | ||
| 806 | } | ||
| 807 | |||
| 808 | return 0; | ||
| 809 | |||
| 810 | error: | ||
| 811 | if (driver) { | ||
| 812 | gigaset_freedriver(driver); | ||
| 813 | driver = NULL; | ||
| 814 | } | ||
| 815 | platform_driver_unregister(&device_driver); | ||
| 816 | return rc; | ||
| 817 | } | ||
| 818 | |||
| 819 | static void __exit ser_gigaset_exit(void) | ||
| 820 | { | ||
| 821 | int rc; | ||
| 822 | |||
| 823 | gig_dbg(DEBUG_INIT, "%s", __func__); | ||
| 824 | |||
| 825 | if (driver) { | ||
| 826 | gigaset_freedriver(driver); | ||
| 827 | driver = NULL; | ||
| 828 | } | ||
| 829 | |||
| 830 | if ((rc = tty_unregister_ldisc(N_GIGASET_M101)) != 0) | ||
| 831 | err("error %d unregistering line discipline", rc); | ||
| 832 | |||
| 833 | platform_driver_unregister(&device_driver); | ||
| 834 | } | ||
| 835 | |||
| 836 | module_init(ser_gigaset_init); | ||
| 837 | module_exit(ser_gigaset_exit); | ||
diff --git a/include/linux/gigaset_dev.h b/include/linux/gigaset_dev.h index 70ad09c8ad1e..5dc4a316ca37 100644 --- a/include/linux/gigaset_dev.h +++ b/include/linux/gigaset_dev.h | |||
| @@ -9,8 +9,6 @@ | |||
| 9 | * published by the Free Software Foundation; either version 2 of | 9 | * published by the Free Software Foundation; either version 2 of |
| 10 | * the License, or (at your option) any later version. | 10 | * the License, or (at your option) any later version. |
| 11 | * ===================================================================== | 11 | * ===================================================================== |
| 12 | * Version: $Id: gigaset_dev.h,v 1.4.4.4 2005/11/21 22:28:09 hjlipp Exp $ | ||
| 13 | * ===================================================================== | ||
| 14 | */ | 12 | */ |
| 15 | 13 | ||
| 16 | #ifndef GIGASET_INTERFACE_H | 14 | #ifndef GIGASET_INTERFACE_H |
