mirror of
https://github.com/torvalds/linux.git
synced 2026-09-14 16:10:02 +02:00
tty: remove the ipwireless driver
The last mentions of this driver on local Linux and telco support forums date back to 2011. This does not prove that no users remain, but the likelihood is too low to justify continuing to carry the driver in the kernel. The maintainer agreed that the driver should be removed and noted that it is simple enough to build as an external module if a user resurfaces [1]. Remove the driver along with its Kconfig, defconfig, build and MAINTAINERS entries. [1] https://lore.kernel.org/linux-serial/20260218152330.GI26902@suse.cz/ Assisted-by: Codex:GPT-5.6 Signed-off-by: Qingfang Deng <qingfang.deng@linux.dev> Reviewed-by: David Sterba <dsterba@suse.com> Acked-by: David Sterba <dsterba@suse.com> Acked-by: Jiri Kosina <jkosina@suse.com> Link: https://patch.msgid.link/20260803091938.259944-1-qingfang.deng@linux.dev Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
e62745dd11
commit
355b329694
|
|
@ -13699,12 +13699,6 @@ F: include/net/ip_vs.h
|
|||
F: include/uapi/linux/ip_vs.h
|
||||
F: net/netfilter/ipvs/
|
||||
|
||||
IPWIRELESS DRIVER
|
||||
M: Jiri Kosina <jikos@kernel.org>
|
||||
M: David Sterba <dsterba@suse.com>
|
||||
S: Odd Fixes
|
||||
F: drivers/tty/ipwireless/
|
||||
|
||||
IRON DEVICE AUDIO CODEC DRIVERS
|
||||
M: Kiseok Jo <kiseok.jo@irondevice.com>
|
||||
L: linux-sound@vger.kernel.org
|
||||
|
|
|
|||
|
|
@ -572,7 +572,6 @@ CONFIG_PPDEV=m
|
|||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HW_RANDOM_VIRTIO=m
|
||||
CONFIG_NVRAM=y
|
||||
CONFIG_IPWIRELESS=m
|
||||
CONFIG_I2C_CHARDEV=m
|
||||
CONFIG_I2C_HYDRA=m
|
||||
CONFIG_I2C_MPC=m
|
||||
|
|
|
|||
|
|
@ -291,15 +291,6 @@ config GOLDFISH_TTY_EARLY_CONSOLE
|
|||
default y if GOLDFISH_TTY=y
|
||||
select SERIAL_EARLYCON
|
||||
|
||||
config IPWIRELESS
|
||||
tristate "IPWireless 3G UMTS PCMCIA card support"
|
||||
depends on PCMCIA && NETDEVICES && HAS_IOPORT
|
||||
select PPP
|
||||
help
|
||||
This is a driver for 3G UMTS PCMCIA card from IPWireless company. In
|
||||
some countries (for example Czech Republic, T-Mobile ISP) this card
|
||||
is shipped for service called UMTS 4G.
|
||||
|
||||
config N_GSM
|
||||
tristate "GSM MUX line discipline support (EXPERIMENTAL)"
|
||||
depends on NET
|
||||
|
|
|
|||
|
|
@ -26,5 +26,3 @@ obj-$(CONFIG_GOLDFISH_TTY) += goldfish.o
|
|||
obj-$(CONFIG_MIPS_EJTAG_FDC_TTY) += mips_ejtag_fdc.o
|
||||
obj-$(CONFIG_VCC) += vcc.o
|
||||
obj-$(CONFIG_RPMSG_TTY) += rpmsg_tty.o
|
||||
|
||||
obj-y += ipwireless/
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Makefile for the IPWireless driver
|
||||
#
|
||||
|
||||
obj-$(CONFIG_IPWIRELESS) += ipwireless.o
|
||||
|
||||
ipwireless-y := hardware.o main.o network.o tty.o
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -1,63 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#ifndef _IPWIRELESS_CS_HARDWARE_H_
|
||||
#define _IPWIRELESS_CS_HARDWARE_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
|
||||
#define IPW_CONTROL_LINE_CTS 0x0001
|
||||
#define IPW_CONTROL_LINE_DCD 0x0002
|
||||
#define IPW_CONTROL_LINE_DSR 0x0004
|
||||
#define IPW_CONTROL_LINE_RI 0x0008
|
||||
#define IPW_CONTROL_LINE_DTR 0x0010
|
||||
#define IPW_CONTROL_LINE_RTS 0x0020
|
||||
|
||||
struct ipw_hardware;
|
||||
struct ipw_network;
|
||||
|
||||
struct ipw_hardware *ipwireless_hardware_create(void);
|
||||
void ipwireless_hardware_free(struct ipw_hardware *hw);
|
||||
irqreturn_t ipwireless_interrupt(int irq, void *dev_id);
|
||||
int ipwireless_set_DTR(struct ipw_hardware *hw, unsigned int channel_idx,
|
||||
int state);
|
||||
int ipwireless_set_RTS(struct ipw_hardware *hw, unsigned int channel_idx,
|
||||
int state);
|
||||
int ipwireless_send_packet(struct ipw_hardware *hw,
|
||||
unsigned int channel_idx,
|
||||
const unsigned char *data,
|
||||
unsigned int length,
|
||||
void (*packet_sent_callback) (void *cb,
|
||||
unsigned int length),
|
||||
void *sent_cb_data);
|
||||
void ipwireless_associate_network(struct ipw_hardware *hw,
|
||||
struct ipw_network *net);
|
||||
void ipwireless_stop_interrupts(struct ipw_hardware *hw);
|
||||
void ipwireless_init_hardware_v1(struct ipw_hardware *hw,
|
||||
unsigned int base_port,
|
||||
void __iomem *attr_memory,
|
||||
void __iomem *common_memory,
|
||||
int is_v2_card,
|
||||
void (*reboot_cb) (void *data),
|
||||
void *reboot_cb_data);
|
||||
void ipwireless_init_hardware_v2_v3(struct ipw_hardware *hw);
|
||||
void ipwireless_sleep(unsigned int tenths);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,356 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#include "hardware.h"
|
||||
#include "network.h"
|
||||
#include "main.h"
|
||||
#include "tty.h"
|
||||
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <pcmcia/cisreg.h>
|
||||
#include <pcmcia/device_id.h>
|
||||
#include <pcmcia/ss.h>
|
||||
#include <pcmcia/ds.h>
|
||||
|
||||
static const struct pcmcia_device_id ipw_ids[] = {
|
||||
PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0100),
|
||||
PCMCIA_DEVICE_MANF_CARD(0x02f2, 0x0200),
|
||||
PCMCIA_DEVICE_NULL
|
||||
};
|
||||
MODULE_DEVICE_TABLE(pcmcia, ipw_ids);
|
||||
|
||||
static void ipwireless_detach(struct pcmcia_device *link);
|
||||
|
||||
/*
|
||||
* Module params
|
||||
*/
|
||||
/* Debug mode: more verbose, print sent/recv bytes */
|
||||
int ipwireless_debug;
|
||||
int ipwireless_loopback;
|
||||
int ipwireless_out_queue = 10;
|
||||
|
||||
module_param_named(debug, ipwireless_debug, int, 0);
|
||||
module_param_named(loopback, ipwireless_loopback, int, 0);
|
||||
module_param_named(out_queue, ipwireless_out_queue, int, 0);
|
||||
MODULE_PARM_DESC(debug, "switch on debug messages [0]");
|
||||
MODULE_PARM_DESC(loopback,
|
||||
"debug: enable ras_raw channel [0]");
|
||||
MODULE_PARM_DESC(out_queue, "debug: set size of outgoing PPP queue [10]");
|
||||
|
||||
/* Executes in process context. */
|
||||
static void signalled_reboot_work(struct work_struct *work_reboot)
|
||||
{
|
||||
struct ipw_dev *ipw = container_of(work_reboot, struct ipw_dev,
|
||||
work_reboot);
|
||||
struct pcmcia_device *link = ipw->link;
|
||||
pcmcia_reset_card(link->socket);
|
||||
}
|
||||
|
||||
static void signalled_reboot_callback(void *callback_data)
|
||||
{
|
||||
struct ipw_dev *ipw = (struct ipw_dev *) callback_data;
|
||||
|
||||
/* Delegate to process context. */
|
||||
schedule_work(&ipw->work_reboot);
|
||||
}
|
||||
|
||||
static int ipwireless_probe(struct pcmcia_device *p_dev, void *priv_data)
|
||||
{
|
||||
struct ipw_dev *ipw = priv_data;
|
||||
int ret;
|
||||
|
||||
p_dev->resource[0]->flags &= ~IO_DATA_PATH_WIDTH;
|
||||
p_dev->resource[0]->flags |= IO_DATA_PATH_WIDTH_AUTO;
|
||||
|
||||
/* 0x40 causes it to generate level mode interrupts. */
|
||||
/* 0x04 enables IREQ pin. */
|
||||
p_dev->config_index |= 0x44;
|
||||
p_dev->io_lines = 16;
|
||||
ret = pcmcia_request_io(p_dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!request_region(p_dev->resource[0]->start,
|
||||
resource_size(p_dev->resource[0]),
|
||||
IPWIRELESS_PCCARD_NAME)) {
|
||||
ret = -EBUSY;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
p_dev->resource[2]->flags |=
|
||||
WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_CM | WIN_ENABLE;
|
||||
|
||||
ret = pcmcia_request_window(p_dev, p_dev->resource[2], 0);
|
||||
if (ret != 0)
|
||||
goto exit1;
|
||||
|
||||
ret = pcmcia_map_mem_page(p_dev, p_dev->resource[2], p_dev->card_addr);
|
||||
if (ret != 0)
|
||||
goto exit1;
|
||||
|
||||
ipw->is_v2_card = resource_size(p_dev->resource[2]) == 0x100;
|
||||
|
||||
ipw->common_memory = ioremap(p_dev->resource[2]->start,
|
||||
resource_size(p_dev->resource[2]));
|
||||
if (!ipw->common_memory) {
|
||||
ret = -ENOMEM;
|
||||
goto exit1;
|
||||
}
|
||||
if (!request_mem_region(p_dev->resource[2]->start,
|
||||
resource_size(p_dev->resource[2]),
|
||||
IPWIRELESS_PCCARD_NAME)) {
|
||||
ret = -EBUSY;
|
||||
goto exit2;
|
||||
}
|
||||
|
||||
p_dev->resource[3]->flags |= WIN_DATA_WIDTH_16 | WIN_MEMORY_TYPE_AM |
|
||||
WIN_ENABLE;
|
||||
p_dev->resource[3]->end = 0; /* this used to be 0x1000 */
|
||||
ret = pcmcia_request_window(p_dev, p_dev->resource[3], 0);
|
||||
if (ret != 0)
|
||||
goto exit3;
|
||||
|
||||
ret = pcmcia_map_mem_page(p_dev, p_dev->resource[3], 0);
|
||||
if (ret != 0)
|
||||
goto exit3;
|
||||
|
||||
ipw->attr_memory = ioremap(p_dev->resource[3]->start,
|
||||
resource_size(p_dev->resource[3]));
|
||||
if (!ipw->attr_memory) {
|
||||
ret = -ENOMEM;
|
||||
goto exit3;
|
||||
}
|
||||
if (!request_mem_region(p_dev->resource[3]->start,
|
||||
resource_size(p_dev->resource[3]),
|
||||
IPWIRELESS_PCCARD_NAME)) {
|
||||
ret = -EBUSY;
|
||||
goto exit4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
exit4:
|
||||
iounmap(ipw->attr_memory);
|
||||
exit3:
|
||||
release_mem_region(p_dev->resource[2]->start,
|
||||
resource_size(p_dev->resource[2]));
|
||||
exit2:
|
||||
iounmap(ipw->common_memory);
|
||||
exit1:
|
||||
release_region(p_dev->resource[0]->start,
|
||||
resource_size(p_dev->resource[0]));
|
||||
exit:
|
||||
pcmcia_disable_device(p_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int config_ipwireless(struct ipw_dev *ipw)
|
||||
{
|
||||
struct pcmcia_device *link = ipw->link;
|
||||
int ret = 0;
|
||||
|
||||
ipw->is_v2_card = 0;
|
||||
link->config_flags |= CONF_AUTO_SET_IO | CONF_AUTO_SET_IOMEM |
|
||||
CONF_ENABLE_IRQ;
|
||||
|
||||
ret = pcmcia_loop_config(link, ipwireless_probe, ipw);
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
INIT_WORK(&ipw->work_reboot, signalled_reboot_work);
|
||||
|
||||
ipwireless_init_hardware_v1(ipw->hardware, link->resource[0]->start,
|
||||
ipw->attr_memory, ipw->common_memory,
|
||||
ipw->is_v2_card, signalled_reboot_callback,
|
||||
ipw);
|
||||
|
||||
ret = pcmcia_request_irq(link, ipwireless_interrupt);
|
||||
if (ret != 0)
|
||||
goto exit;
|
||||
|
||||
printk(KERN_INFO IPWIRELESS_PCCARD_NAME ": Card type %s\n",
|
||||
ipw->is_v2_card ? "V2/V3" : "V1");
|
||||
printk(KERN_INFO IPWIRELESS_PCCARD_NAME
|
||||
": I/O ports %pR, irq %d\n", link->resource[0],
|
||||
(unsigned int) link->irq);
|
||||
if (ipw->attr_memory && ipw->common_memory)
|
||||
printk(KERN_INFO IPWIRELESS_PCCARD_NAME
|
||||
": attr memory %pR, common memory %pR\n",
|
||||
link->resource[3],
|
||||
link->resource[2]);
|
||||
|
||||
ipw->network = ipwireless_network_create(ipw->hardware);
|
||||
if (!ipw->network)
|
||||
goto exit;
|
||||
|
||||
ipw->tty = ipwireless_tty_create(ipw->hardware, ipw->network);
|
||||
if (!ipw->tty)
|
||||
goto exit;
|
||||
|
||||
ipwireless_init_hardware_v2_v3(ipw->hardware);
|
||||
|
||||
/*
|
||||
* Do the RequestConfiguration last, because it enables interrupts.
|
||||
* Then we don't get any interrupts before we're ready for them.
|
||||
*/
|
||||
ret = pcmcia_enable_device(link);
|
||||
if (ret != 0)
|
||||
goto exit;
|
||||
|
||||
return 0;
|
||||
|
||||
exit:
|
||||
if (ipw->common_memory) {
|
||||
release_mem_region(link->resource[2]->start,
|
||||
resource_size(link->resource[2]));
|
||||
iounmap(ipw->common_memory);
|
||||
}
|
||||
if (ipw->attr_memory) {
|
||||
release_mem_region(link->resource[3]->start,
|
||||
resource_size(link->resource[3]));
|
||||
iounmap(ipw->attr_memory);
|
||||
}
|
||||
pcmcia_disable_device(link);
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void release_ipwireless(struct ipw_dev *ipw)
|
||||
{
|
||||
release_region(ipw->link->resource[0]->start,
|
||||
resource_size(ipw->link->resource[0]));
|
||||
if (ipw->common_memory) {
|
||||
release_mem_region(ipw->link->resource[2]->start,
|
||||
resource_size(ipw->link->resource[2]));
|
||||
iounmap(ipw->common_memory);
|
||||
}
|
||||
if (ipw->attr_memory) {
|
||||
release_mem_region(ipw->link->resource[3]->start,
|
||||
resource_size(ipw->link->resource[3]));
|
||||
iounmap(ipw->attr_memory);
|
||||
}
|
||||
pcmcia_disable_device(ipw->link);
|
||||
}
|
||||
|
||||
/*
|
||||
* ipwireless_attach() creates an "instance" of the driver, allocating
|
||||
* local data structures for one device (one interface). The device
|
||||
* is registered with Card Services.
|
||||
*
|
||||
* The pcmcia_device structure is initialized, but we don't actually
|
||||
* configure the card at this point -- we wait until we receive a
|
||||
* card insertion event.
|
||||
*/
|
||||
static int ipwireless_attach(struct pcmcia_device *link)
|
||||
{
|
||||
struct ipw_dev *ipw;
|
||||
int ret;
|
||||
|
||||
ipw = kzalloc_obj(struct ipw_dev);
|
||||
if (!ipw)
|
||||
return -ENOMEM;
|
||||
|
||||
ipw->link = link;
|
||||
link->priv = ipw;
|
||||
|
||||
ipw->hardware = ipwireless_hardware_create();
|
||||
if (!ipw->hardware) {
|
||||
kfree(ipw);
|
||||
return -ENOMEM;
|
||||
}
|
||||
/* RegisterClient will call config_ipwireless */
|
||||
|
||||
ret = config_ipwireless(ipw);
|
||||
|
||||
if (ret != 0) {
|
||||
ipwireless_detach(link);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This deletes a driver "instance". The device is de-registered with
|
||||
* Card Services. If it has been released, all local data structures
|
||||
* are freed. Otherwise, the structures will be freed when the device
|
||||
* is released.
|
||||
*/
|
||||
static void ipwireless_detach(struct pcmcia_device *link)
|
||||
{
|
||||
struct ipw_dev *ipw = link->priv;
|
||||
|
||||
release_ipwireless(ipw);
|
||||
|
||||
if (ipw->tty != NULL)
|
||||
ipwireless_tty_free(ipw->tty);
|
||||
if (ipw->network != NULL)
|
||||
ipwireless_network_free(ipw->network);
|
||||
if (ipw->hardware != NULL)
|
||||
ipwireless_hardware_free(ipw->hardware);
|
||||
kfree(ipw);
|
||||
}
|
||||
|
||||
static struct pcmcia_driver me = {
|
||||
.owner = THIS_MODULE,
|
||||
.probe = ipwireless_attach,
|
||||
.remove = ipwireless_detach,
|
||||
.name = IPWIRELESS_PCCARD_NAME,
|
||||
.id_table = ipw_ids
|
||||
};
|
||||
|
||||
/*
|
||||
* Module insertion : initialisation of the module.
|
||||
* Register the card with cardmgr...
|
||||
*/
|
||||
static int __init init_ipwireless(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ipwireless_tty_init();
|
||||
if (ret != 0)
|
||||
return ret;
|
||||
|
||||
ret = pcmcia_register_driver(&me);
|
||||
if (ret != 0)
|
||||
ipwireless_tty_release();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Module removal
|
||||
*/
|
||||
static void __exit exit_ipwireless(void)
|
||||
{
|
||||
pcmcia_unregister_driver(&me);
|
||||
ipwireless_tty_release();
|
||||
}
|
||||
|
||||
module_init(init_ipwireless);
|
||||
module_exit(exit_ipwireless);
|
||||
|
||||
MODULE_AUTHOR(IPWIRELESS_PCMCIA_AUTHOR);
|
||||
MODULE_DESCRIPTION(IPWIRELESS_PCCARD_NAME " " IPWIRELESS_PCMCIA_VERSION);
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -1,66 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#ifndef _IPWIRELESS_CS_H_
|
||||
#define _IPWIRELESS_CS_H_
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/types.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ds.h>
|
||||
|
||||
#include "hardware.h"
|
||||
|
||||
#define IPWIRELESS_PCCARD_NAME "ipwireless"
|
||||
#define IPWIRELESS_PCMCIA_VERSION "1.1"
|
||||
#define IPWIRELESS_PCMCIA_AUTHOR \
|
||||
"Stephen Blackheath, Ben Martel, Jiri Kosina and David Sterba"
|
||||
|
||||
#define IPWIRELESS_TX_QUEUE_SIZE 262144
|
||||
#define IPWIRELESS_RX_QUEUE_SIZE 262144
|
||||
|
||||
#define IPWIRELESS_STATE_DEBUG
|
||||
|
||||
struct ipw_hardware;
|
||||
struct ipw_network;
|
||||
struct ipw_tty;
|
||||
|
||||
struct ipw_dev {
|
||||
struct pcmcia_device *link;
|
||||
int is_v2_card;
|
||||
|
||||
void __iomem *attr_memory;
|
||||
|
||||
void __iomem *common_memory;
|
||||
|
||||
/* Hardware context */
|
||||
struct ipw_hardware *hardware;
|
||||
/* Network layer context */
|
||||
struct ipw_network *network;
|
||||
/* TTY device context */
|
||||
struct ipw_tty *tty;
|
||||
struct work_struct work_reboot;
|
||||
};
|
||||
|
||||
/* Module parametres */
|
||||
extern int ipwireless_debug;
|
||||
extern int ipwireless_loopback;
|
||||
extern int ipwireless_out_queue;
|
||||
|
||||
#endif
|
||||
|
|
@ -1,517 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/ppp_channel.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/ppp-ioctl.h>
|
||||
#include <linux/skbuff.h>
|
||||
|
||||
#include "network.h"
|
||||
#include "hardware.h"
|
||||
#include "main.h"
|
||||
#include "tty.h"
|
||||
|
||||
#define MAX_ASSOCIATED_TTYS 2
|
||||
|
||||
#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP)
|
||||
|
||||
struct ipw_network {
|
||||
/* Hardware context, used for calls to hardware layer. */
|
||||
struct ipw_hardware *hardware;
|
||||
/* Context for kernel 'generic_ppp' functionality */
|
||||
struct ppp_channel *ppp_channel;
|
||||
/* tty context connected with IPW console */
|
||||
struct ipw_tty *associated_ttys[NO_OF_IPW_CHANNELS][MAX_ASSOCIATED_TTYS];
|
||||
/* True if ppp needs waking up once we're ready to xmit */
|
||||
int ppp_blocked;
|
||||
/* Number of packets queued up in hardware module. */
|
||||
int outgoing_packets_queued;
|
||||
/* Spinlock to avoid interrupts during shutdown */
|
||||
spinlock_t lock;
|
||||
struct mutex close_lock;
|
||||
|
||||
/* PPP ioctl data, not actually used anywere */
|
||||
unsigned int flags;
|
||||
unsigned int rbits;
|
||||
u32 xaccm[8];
|
||||
u32 raccm;
|
||||
int mru;
|
||||
|
||||
int shutting_down;
|
||||
unsigned int ras_control_lines;
|
||||
|
||||
struct work_struct work_go_online;
|
||||
struct work_struct work_go_offline;
|
||||
};
|
||||
|
||||
static void notify_packet_sent(void *callback_data, unsigned int packet_length)
|
||||
{
|
||||
struct ipw_network *network = callback_data;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
network->outgoing_packets_queued--;
|
||||
if (network->ppp_channel != NULL) {
|
||||
if (network->ppp_blocked) {
|
||||
network->ppp_blocked = 0;
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
ppp_output_wakeup(network->ppp_channel);
|
||||
if (ipwireless_debug)
|
||||
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
|
||||
": ppp unblocked\n");
|
||||
} else
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
} else
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by the ppp system when it has a packet to send to the hardware.
|
||||
*/
|
||||
static int ipwireless_ppp_start_xmit(struct ppp_channel *ppp_channel,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct ipw_network *network = ppp_channel->private;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
if (network->outgoing_packets_queued < ipwireless_out_queue) {
|
||||
unsigned char *buf;
|
||||
static unsigned char header[] = {
|
||||
PPP_ALLSTATIONS, /* 0xff */
|
||||
PPP_UI, /* 0x03 */
|
||||
};
|
||||
int ret;
|
||||
|
||||
network->outgoing_packets_queued++;
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
|
||||
/*
|
||||
* If we have the requested amount of headroom in the skb we
|
||||
* were handed, then we can add the header efficiently.
|
||||
*/
|
||||
if (skb_headroom(skb) >= 2) {
|
||||
memcpy(skb_push(skb, 2), header, 2);
|
||||
ret = ipwireless_send_packet(network->hardware,
|
||||
IPW_CHANNEL_RAS, skb->data,
|
||||
skb->len,
|
||||
notify_packet_sent,
|
||||
network);
|
||||
if (ret < 0) {
|
||||
skb_pull(skb, 2);
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
/* Otherwise (rarely) we do it inefficiently. */
|
||||
buf = kmalloc(skb->len + 2, GFP_ATOMIC);
|
||||
if (!buf)
|
||||
return 0;
|
||||
memcpy(buf + 2, skb->data, skb->len);
|
||||
memcpy(buf, header, 2);
|
||||
ret = ipwireless_send_packet(network->hardware,
|
||||
IPW_CHANNEL_RAS, buf,
|
||||
skb->len + 2,
|
||||
notify_packet_sent,
|
||||
network);
|
||||
kfree(buf);
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
return 1;
|
||||
} else {
|
||||
/*
|
||||
* Otherwise reject the packet, and flag that the ppp system
|
||||
* needs to be unblocked once we are ready to send.
|
||||
*/
|
||||
network->ppp_blocked = 1;
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
if (ipwireless_debug)
|
||||
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": ppp blocked\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Handle an ioctl call that has come in via ppp. (copy of ppp_async_ioctl() */
|
||||
static int ipwireless_ppp_ioctl(struct ppp_channel *ppp_channel,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct ipw_network *network = ppp_channel->private;
|
||||
int err, val;
|
||||
u32 accm[8];
|
||||
int __user *user_arg = (int __user *) arg;
|
||||
|
||||
err = -EFAULT;
|
||||
switch (cmd) {
|
||||
case PPPIOCGFLAGS:
|
||||
val = network->flags | network->rbits;
|
||||
if (put_user(val, user_arg))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCSFLAGS:
|
||||
if (get_user(val, user_arg))
|
||||
break;
|
||||
network->flags = val & ~SC_RCV_BITS;
|
||||
network->rbits = val & SC_RCV_BITS;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGASYNCMAP:
|
||||
if (put_user(network->xaccm[0], user_arg))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCSASYNCMAP:
|
||||
if (get_user(network->xaccm[0], user_arg))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGRASYNCMAP:
|
||||
if (put_user(network->raccm, user_arg))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCSRASYNCMAP:
|
||||
if (get_user(network->raccm, user_arg))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGXASYNCMAP:
|
||||
if (copy_to_user((void __user *) arg, network->xaccm,
|
||||
sizeof(network->xaccm)))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCSXASYNCMAP:
|
||||
if (copy_from_user(accm, (void __user *) arg, sizeof(accm)))
|
||||
break;
|
||||
accm[2] &= ~0x40000000U; /* can't escape 0x5e */
|
||||
accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */
|
||||
memcpy(network->xaccm, accm, sizeof(network->xaccm));
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCGMRU:
|
||||
if (put_user(network->mru, user_arg))
|
||||
break;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
case PPPIOCSMRU:
|
||||
if (get_user(val, user_arg))
|
||||
break;
|
||||
if (val < PPP_MRU)
|
||||
val = PPP_MRU;
|
||||
network->mru = val;
|
||||
err = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENOTTY;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct ppp_channel_ops ipwireless_ppp_channel_ops = {
|
||||
.start_xmit = ipwireless_ppp_start_xmit,
|
||||
.ioctl = ipwireless_ppp_ioctl
|
||||
};
|
||||
|
||||
static void do_go_online(struct work_struct *work_go_online)
|
||||
{
|
||||
struct ipw_network *network =
|
||||
container_of(work_go_online, struct ipw_network,
|
||||
work_go_online);
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
if (!network->ppp_channel) {
|
||||
struct ppp_channel *channel;
|
||||
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
channel = kzalloc_obj(struct ppp_channel);
|
||||
if (!channel) {
|
||||
printk(KERN_ERR IPWIRELESS_PCCARD_NAME
|
||||
": unable to allocate PPP channel\n");
|
||||
return;
|
||||
}
|
||||
channel->private = network;
|
||||
channel->mtu = 16384; /* Wild guess */
|
||||
channel->hdrlen = 2;
|
||||
channel->ops = &ipwireless_ppp_channel_ops;
|
||||
|
||||
network->flags = 0;
|
||||
network->rbits = 0;
|
||||
network->mru = PPP_MRU;
|
||||
memset(network->xaccm, 0, sizeof(network->xaccm));
|
||||
network->xaccm[0] = ~0U;
|
||||
network->xaccm[3] = 0x60000000U;
|
||||
network->raccm = ~0U;
|
||||
if (ppp_register_channel(channel) < 0) {
|
||||
printk(KERN_ERR IPWIRELESS_PCCARD_NAME
|
||||
": unable to register PPP channel\n");
|
||||
kfree(channel);
|
||||
return;
|
||||
}
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
network->ppp_channel = channel;
|
||||
}
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
}
|
||||
|
||||
static void do_go_offline(struct work_struct *work_go_offline)
|
||||
{
|
||||
struct ipw_network *network =
|
||||
container_of(work_go_offline, struct ipw_network,
|
||||
work_go_offline);
|
||||
unsigned long flags;
|
||||
|
||||
mutex_lock(&network->close_lock);
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
if (network->ppp_channel != NULL) {
|
||||
struct ppp_channel *channel = network->ppp_channel;
|
||||
|
||||
network->ppp_channel = NULL;
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
mutex_unlock(&network->close_lock);
|
||||
ppp_unregister_channel(channel);
|
||||
} else {
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
mutex_unlock(&network->close_lock);
|
||||
}
|
||||
}
|
||||
|
||||
void ipwireless_network_notify_control_line_change(struct ipw_network *network,
|
||||
unsigned int channel_idx,
|
||||
unsigned int control_lines,
|
||||
unsigned int changed_mask)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (channel_idx == IPW_CHANNEL_RAS)
|
||||
network->ras_control_lines = control_lines;
|
||||
|
||||
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) {
|
||||
struct ipw_tty *tty =
|
||||
network->associated_ttys[channel_idx][i];
|
||||
|
||||
/*
|
||||
* If it's associated with a tty (other than the RAS channel
|
||||
* when we're online), then send the data to that tty. The RAS
|
||||
* channel's data is handled above - it always goes through
|
||||
* ppp_generic.
|
||||
*/
|
||||
if (tty)
|
||||
ipwireless_tty_notify_control_line_change(tty,
|
||||
channel_idx,
|
||||
control_lines,
|
||||
changed_mask);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Some versions of firmware stuff packets with 0xff 0x03 (PPP: ALLSTATIONS, UI)
|
||||
* bytes, which are required on sent packet, but not always present on received
|
||||
* packets
|
||||
*/
|
||||
static struct sk_buff *ipw_packet_received_skb(unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
|
||||
if (length > 2 && data[0] == PPP_ALLSTATIONS && data[1] == PPP_UI) {
|
||||
length -= 2;
|
||||
data += 2;
|
||||
}
|
||||
|
||||
skb = dev_alloc_skb(length + 4);
|
||||
if (skb == NULL)
|
||||
return NULL;
|
||||
skb_reserve(skb, 2);
|
||||
skb_put_data(skb, data, length);
|
||||
|
||||
return skb;
|
||||
}
|
||||
|
||||
void ipwireless_network_packet_received(struct ipw_network *network,
|
||||
unsigned int channel_idx,
|
||||
unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
int i;
|
||||
unsigned long flags;
|
||||
|
||||
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++) {
|
||||
struct ipw_tty *tty = network->associated_ttys[channel_idx][i];
|
||||
|
||||
if (!tty)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* If it's associated with a tty (other than the RAS channel
|
||||
* when we're online), then send the data to that tty. The RAS
|
||||
* channel's data is handled above - it always goes through
|
||||
* ppp_generic.
|
||||
*/
|
||||
if (channel_idx == IPW_CHANNEL_RAS
|
||||
&& (network->ras_control_lines &
|
||||
IPW_CONTROL_LINE_DCD) != 0
|
||||
&& ipwireless_tty_is_modem(tty)) {
|
||||
/*
|
||||
* If data came in on the RAS channel and this tty is
|
||||
* the modem tty, and we are online, then we send it to
|
||||
* the PPP layer.
|
||||
*/
|
||||
mutex_lock(&network->close_lock);
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
if (network->ppp_channel != NULL) {
|
||||
struct sk_buff *skb;
|
||||
|
||||
spin_unlock_irqrestore(&network->lock,
|
||||
flags);
|
||||
|
||||
/* Send the data to the ppp_generic module. */
|
||||
skb = ipw_packet_received_skb(data, length);
|
||||
if (skb)
|
||||
ppp_input(network->ppp_channel, skb);
|
||||
} else
|
||||
spin_unlock_irqrestore(&network->lock,
|
||||
flags);
|
||||
mutex_unlock(&network->close_lock);
|
||||
}
|
||||
/* Otherwise we send it out the tty. */
|
||||
else
|
||||
ipwireless_tty_received(tty, data, length);
|
||||
}
|
||||
}
|
||||
|
||||
struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw)
|
||||
{
|
||||
struct ipw_network *network =
|
||||
kzalloc_obj(struct ipw_network);
|
||||
|
||||
if (!network)
|
||||
return NULL;
|
||||
|
||||
spin_lock_init(&network->lock);
|
||||
mutex_init(&network->close_lock);
|
||||
|
||||
network->hardware = hw;
|
||||
|
||||
INIT_WORK(&network->work_go_online, do_go_online);
|
||||
INIT_WORK(&network->work_go_offline, do_go_offline);
|
||||
|
||||
ipwireless_associate_network(hw, network);
|
||||
|
||||
return network;
|
||||
}
|
||||
|
||||
void ipwireless_network_free(struct ipw_network *network)
|
||||
{
|
||||
network->shutting_down = 1;
|
||||
|
||||
ipwireless_ppp_close(network);
|
||||
flush_work(&network->work_go_online);
|
||||
flush_work(&network->work_go_offline);
|
||||
|
||||
ipwireless_stop_interrupts(network->hardware);
|
||||
ipwireless_associate_network(network->hardware, NULL);
|
||||
|
||||
kfree(network);
|
||||
}
|
||||
|
||||
void ipwireless_associate_network_tty(struct ipw_network *network,
|
||||
unsigned int channel_idx,
|
||||
struct ipw_tty *tty)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++)
|
||||
if (network->associated_ttys[channel_idx][i] == NULL) {
|
||||
network->associated_ttys[channel_idx][i] = tty;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ipwireless_disassociate_network_ttys(struct ipw_network *network,
|
||||
unsigned int channel_idx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < MAX_ASSOCIATED_TTYS; i++)
|
||||
network->associated_ttys[channel_idx][i] = NULL;
|
||||
}
|
||||
|
||||
void ipwireless_ppp_open(struct ipw_network *network)
|
||||
{
|
||||
if (ipwireless_debug)
|
||||
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": online\n");
|
||||
schedule_work(&network->work_go_online);
|
||||
}
|
||||
|
||||
void ipwireless_ppp_close(struct ipw_network *network)
|
||||
{
|
||||
/* Disconnect from the wireless network. */
|
||||
if (ipwireless_debug)
|
||||
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME ": offline\n");
|
||||
schedule_work(&network->work_go_offline);
|
||||
}
|
||||
|
||||
int ipwireless_ppp_channel_index(struct ipw_network *network)
|
||||
{
|
||||
int ret = -1;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
if (network->ppp_channel != NULL)
|
||||
ret = ppp_channel_index(network->ppp_channel);
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipwireless_ppp_unit_number(struct ipw_network *network)
|
||||
{
|
||||
int ret = -1;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&network->lock, flags);
|
||||
if (network->ppp_channel != NULL)
|
||||
ret = ppp_unit_number(network->ppp_channel);
|
||||
spin_unlock_irqrestore(&network->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ipwireless_ppp_mru(const struct ipw_network *network)
|
||||
{
|
||||
return network->mru;
|
||||
}
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#ifndef _IPWIRELESS_CS_NETWORK_H_
|
||||
#define _IPWIRELESS_CS_NETWORK_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct ipw_network;
|
||||
struct ipw_tty;
|
||||
struct ipw_hardware;
|
||||
|
||||
/* Definitions of the different channels on the PCMCIA UE */
|
||||
#define IPW_CHANNEL_RAS 0
|
||||
#define IPW_CHANNEL_DIALLER 1
|
||||
#define IPW_CHANNEL_CONSOLE 2
|
||||
#define NO_OF_IPW_CHANNELS 5
|
||||
|
||||
void ipwireless_network_notify_control_line_change(struct ipw_network *net,
|
||||
unsigned int channel_idx, unsigned int control_lines,
|
||||
unsigned int control_mask);
|
||||
void ipwireless_network_packet_received(struct ipw_network *net,
|
||||
unsigned int channel_idx, unsigned char *data,
|
||||
unsigned int length);
|
||||
struct ipw_network *ipwireless_network_create(struct ipw_hardware *hw);
|
||||
void ipwireless_network_free(struct ipw_network *net);
|
||||
void ipwireless_associate_network_tty(struct ipw_network *net,
|
||||
unsigned int channel_idx, struct ipw_tty *tty);
|
||||
void ipwireless_disassociate_network_ttys(struct ipw_network *net,
|
||||
unsigned int channel_idx);
|
||||
|
||||
void ipwireless_ppp_open(struct ipw_network *net);
|
||||
|
||||
void ipwireless_ppp_close(struct ipw_network *net);
|
||||
int ipwireless_ppp_channel_index(struct ipw_network *net);
|
||||
int ipwireless_ppp_unit_number(struct ipw_network *net);
|
||||
int ipwireless_ppp_mru(const struct ipw_network *net);
|
||||
|
||||
#endif
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#ifndef _IPWIRELESS_CS_SETUP_PROTOCOL_H_
|
||||
#define _IPWIRELESS_CS_SETUP_PROTOCOL_H_
|
||||
|
||||
/* Version of the setup protocol and transport protocols */
|
||||
#define TL_SETUP_VERSION 1
|
||||
|
||||
#define TL_SETUP_VERSION_QRY_TMO 1000
|
||||
#define TL_SETUP_MAX_VERSION_QRY 30
|
||||
|
||||
/* Message numbers 0-9 are obsoleted and must not be reused! */
|
||||
#define TL_SETUP_SIGNO_GET_VERSION_QRY 10
|
||||
#define TL_SETUP_SIGNO_GET_VERSION_RSP 11
|
||||
#define TL_SETUP_SIGNO_CONFIG_MSG 12
|
||||
#define TL_SETUP_SIGNO_CONFIG_DONE_MSG 13
|
||||
#define TL_SETUP_SIGNO_OPEN_MSG 14
|
||||
#define TL_SETUP_SIGNO_CLOSE_MSG 15
|
||||
|
||||
#define TL_SETUP_SIGNO_INFO_MSG 20
|
||||
#define TL_SETUP_SIGNO_INFO_MSG_ACK 21
|
||||
|
||||
#define TL_SETUP_SIGNO_REBOOT_MSG 22
|
||||
#define TL_SETUP_SIGNO_REBOOT_MSG_ACK 23
|
||||
|
||||
/* Synchronous start-messages */
|
||||
struct tl_setup_get_version_qry {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_GET_VERSION_QRY */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct tl_setup_get_version_rsp {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_GET_VERSION_RSP */
|
||||
unsigned char version; /* TL_SETUP_VERSION */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct tl_setup_config_msg {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_CONFIG_MSG */
|
||||
unsigned char port_no;
|
||||
unsigned char prio_data;
|
||||
unsigned char prio_ctrl;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct tl_setup_config_done_msg {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_CONFIG_DONE_MSG */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* Asynchronous messages */
|
||||
struct tl_setup_open_msg {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_OPEN_MSG */
|
||||
unsigned char port_no;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct tl_setup_close_msg {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_CLOSE_MSG */
|
||||
unsigned char port_no;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* Driver type - for use in tl_setup_info_msg.driver_type */
|
||||
#define COMM_DRIVER 0
|
||||
#define NDISWAN_DRIVER 1
|
||||
#define NDISWAN_DRIVER_MAJOR_VERSION 2
|
||||
#define NDISWAN_DRIVER_MINOR_VERSION 0
|
||||
|
||||
/*
|
||||
* It should not matter when this message comes over as we just store the
|
||||
* results and send the ACK.
|
||||
*/
|
||||
struct tl_setup_info_msg {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_INFO_MSG */
|
||||
unsigned char driver_type;
|
||||
unsigned char major_version;
|
||||
unsigned char minor_version;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct tl_setup_info_msgAck {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_INFO_MSG_ACK */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
struct TlSetupRebootMsgAck {
|
||||
unsigned char sig_no; /* TL_SETUP_SIGNO_REBOOT_MSG_ACK */
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
/* Define a union of all the msgs that the driver can receive from the card.*/
|
||||
union ipw_setup_rx_msg {
|
||||
unsigned char sig_no;
|
||||
struct tl_setup_get_version_rsp version_rsp_msg;
|
||||
struct tl_setup_open_msg open_msg;
|
||||
struct tl_setup_close_msg close_msg;
|
||||
struct tl_setup_info_msg InfoMsg;
|
||||
struct tl_setup_info_msgAck info_msg_ack;
|
||||
} __attribute__ ((__packed__));
|
||||
|
||||
#endif /* _IPWIRELESS_CS_SETUP_PROTOCOL_H_ */
|
||||
|
|
@ -1,627 +0,0 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/ppp_defs.h>
|
||||
#include <linux/if.h>
|
||||
#include <linux/ppp-ioctl.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/serial.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/tty.h>
|
||||
#include <linux/tty_driver.h>
|
||||
#include <linux/tty_flip.h>
|
||||
#include <linux/uaccess.h>
|
||||
|
||||
#include "tty.h"
|
||||
#include "network.h"
|
||||
#include "hardware.h"
|
||||
#include "main.h"
|
||||
|
||||
#define IPWIRELESS_PCMCIA_START (0)
|
||||
#define IPWIRELESS_PCMCIA_MINORS (24)
|
||||
#define IPWIRELESS_PCMCIA_MINOR_RANGE (8)
|
||||
|
||||
#define TTYTYPE_MODEM (0)
|
||||
#define TTYTYPE_MONITOR (1)
|
||||
#define TTYTYPE_RAS_RAW (2)
|
||||
|
||||
struct ipw_tty {
|
||||
struct tty_port port;
|
||||
int index;
|
||||
struct ipw_hardware *hardware;
|
||||
unsigned int channel_idx;
|
||||
unsigned int secondary_channel_idx;
|
||||
int tty_type;
|
||||
struct ipw_network *network;
|
||||
unsigned int control_lines;
|
||||
struct mutex ipw_tty_mutex;
|
||||
int tx_bytes_queued;
|
||||
};
|
||||
|
||||
static struct ipw_tty *ttys[IPWIRELESS_PCMCIA_MINORS];
|
||||
|
||||
static struct tty_driver *ipw_tty_driver;
|
||||
|
||||
static char *tty_type_name(int tty_type)
|
||||
{
|
||||
static char *channel_names[] = {
|
||||
"modem",
|
||||
"monitor",
|
||||
"RAS-raw"
|
||||
};
|
||||
|
||||
return channel_names[tty_type];
|
||||
}
|
||||
|
||||
static struct ipw_tty *get_tty(int index)
|
||||
{
|
||||
/*
|
||||
* The 'ras_raw' channel is only available when 'loopback' mode
|
||||
* is enabled.
|
||||
* Number of minor starts with 16 (_RANGE * _RAS_RAW).
|
||||
*/
|
||||
if (!ipwireless_loopback && index >=
|
||||
IPWIRELESS_PCMCIA_MINOR_RANGE * TTYTYPE_RAS_RAW)
|
||||
return NULL;
|
||||
|
||||
return ttys[index];
|
||||
}
|
||||
|
||||
static int ipw_open(struct tty_struct *linux_tty, struct file *filp)
|
||||
{
|
||||
struct ipw_tty *tty = get_tty(linux_tty->index);
|
||||
|
||||
if (!tty)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&tty->ipw_tty_mutex);
|
||||
if (tty->port.count == 0)
|
||||
tty->tx_bytes_queued = 0;
|
||||
|
||||
tty->port.count++;
|
||||
|
||||
tty->port.tty = linux_tty;
|
||||
linux_tty->driver_data = tty;
|
||||
|
||||
if (tty->tty_type == TTYTYPE_MODEM)
|
||||
ipwireless_ppp_open(tty->network);
|
||||
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_ipw_close(struct ipw_tty *tty)
|
||||
{
|
||||
tty->port.count--;
|
||||
|
||||
if (tty->port.count == 0) {
|
||||
struct tty_struct *linux_tty = tty->port.tty;
|
||||
|
||||
if (linux_tty != NULL) {
|
||||
tty->port.tty = NULL;
|
||||
linux_tty->driver_data = NULL;
|
||||
|
||||
if (tty->tty_type == TTYTYPE_MODEM)
|
||||
ipwireless_ppp_close(tty->network);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ipw_hangup(struct tty_struct *linux_tty)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
|
||||
if (!tty)
|
||||
return;
|
||||
|
||||
mutex_lock(&tty->ipw_tty_mutex);
|
||||
if (tty->port.count == 0) {
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
do_ipw_close(tty);
|
||||
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
}
|
||||
|
||||
static void ipw_close(struct tty_struct *linux_tty, struct file *filp)
|
||||
{
|
||||
ipw_hangup(linux_tty);
|
||||
}
|
||||
|
||||
/* Take data received from hardware, and send it out the tty */
|
||||
void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data,
|
||||
unsigned int length)
|
||||
{
|
||||
int work = 0;
|
||||
|
||||
mutex_lock(&tty->ipw_tty_mutex);
|
||||
|
||||
if (!tty->port.count) {
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
return;
|
||||
}
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
|
||||
work = tty_insert_flip_string(&tty->port, data, length);
|
||||
|
||||
if (work != length)
|
||||
printk(KERN_DEBUG IPWIRELESS_PCCARD_NAME
|
||||
": %d chars not inserted to flip buffer!\n",
|
||||
length - work);
|
||||
|
||||
if (work)
|
||||
tty_flip_buffer_push(&tty->port);
|
||||
}
|
||||
|
||||
static void ipw_write_packet_sent_callback(void *callback_data,
|
||||
unsigned int packet_length)
|
||||
{
|
||||
struct ipw_tty *tty = callback_data;
|
||||
|
||||
/*
|
||||
* Packet has been sent, so we subtract the number of bytes from our
|
||||
* tally of outstanding TX bytes.
|
||||
*/
|
||||
tty->tx_bytes_queued -= packet_length;
|
||||
}
|
||||
|
||||
static ssize_t ipw_write(struct tty_struct *linux_tty, const u8 *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
int room, ret;
|
||||
|
||||
if (!tty)
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&tty->ipw_tty_mutex);
|
||||
if (!tty->port.count) {
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
|
||||
if (room < 0)
|
||||
room = 0;
|
||||
/* Don't allow caller to write any more than we have room for */
|
||||
if (count > room)
|
||||
count = room;
|
||||
|
||||
if (count == 0) {
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ipwireless_send_packet(tty->hardware, IPW_CHANNEL_RAS,
|
||||
buf, count,
|
||||
ipw_write_packet_sent_callback, tty);
|
||||
if (ret < 0) {
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
tty->tx_bytes_queued += count;
|
||||
mutex_unlock(&tty->ipw_tty_mutex);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
static unsigned int ipw_write_room(struct tty_struct *linux_tty)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
int room;
|
||||
|
||||
/* FIXME: Exactly how is the tty object locked here .. */
|
||||
if (!tty)
|
||||
return 0;
|
||||
|
||||
if (!tty->port.count)
|
||||
return 0;
|
||||
|
||||
room = IPWIRELESS_TX_QUEUE_SIZE - tty->tx_bytes_queued;
|
||||
if (room < 0)
|
||||
room = 0;
|
||||
|
||||
return room;
|
||||
}
|
||||
|
||||
static int ipwireless_get_serial_info(struct tty_struct *linux_tty,
|
||||
struct serial_struct *ss)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
|
||||
if (!tty)
|
||||
return -ENODEV;
|
||||
|
||||
if (!tty->port.count)
|
||||
return -EINVAL;
|
||||
|
||||
ss->type = PORT_UNKNOWN;
|
||||
ss->line = tty->index;
|
||||
ss->baud_base = 115200;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipwireless_set_serial_info(struct tty_struct *linux_tty,
|
||||
struct serial_struct *ss)
|
||||
{
|
||||
return 0; /* Keeps the PCMCIA scripts happy. */
|
||||
}
|
||||
|
||||
static unsigned int ipw_chars_in_buffer(struct tty_struct *linux_tty)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
|
||||
if (!tty)
|
||||
return 0;
|
||||
|
||||
if (!tty->port.count)
|
||||
return 0;
|
||||
|
||||
return tty->tx_bytes_queued;
|
||||
}
|
||||
|
||||
static int get_control_lines(struct ipw_tty *tty)
|
||||
{
|
||||
unsigned int my = tty->control_lines;
|
||||
unsigned int out = 0;
|
||||
|
||||
if (my & IPW_CONTROL_LINE_RTS)
|
||||
out |= TIOCM_RTS;
|
||||
if (my & IPW_CONTROL_LINE_DTR)
|
||||
out |= TIOCM_DTR;
|
||||
if (my & IPW_CONTROL_LINE_CTS)
|
||||
out |= TIOCM_CTS;
|
||||
if (my & IPW_CONTROL_LINE_DSR)
|
||||
out |= TIOCM_DSR;
|
||||
if (my & IPW_CONTROL_LINE_DCD)
|
||||
out |= TIOCM_CD;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
static int set_control_lines(struct ipw_tty *tty, unsigned int set,
|
||||
unsigned int clear)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (set & TIOCM_RTS) {
|
||||
ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (tty->secondary_channel_idx != -1) {
|
||||
ret = ipwireless_set_RTS(tty->hardware,
|
||||
tty->secondary_channel_idx, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (set & TIOCM_DTR) {
|
||||
ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (tty->secondary_channel_idx != -1) {
|
||||
ret = ipwireless_set_DTR(tty->hardware,
|
||||
tty->secondary_channel_idx, 1);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (clear & TIOCM_RTS) {
|
||||
ret = ipwireless_set_RTS(tty->hardware, tty->channel_idx, 0);
|
||||
if (tty->secondary_channel_idx != -1) {
|
||||
ret = ipwireless_set_RTS(tty->hardware,
|
||||
tty->secondary_channel_idx, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
if (clear & TIOCM_DTR) {
|
||||
ret = ipwireless_set_DTR(tty->hardware, tty->channel_idx, 0);
|
||||
if (tty->secondary_channel_idx != -1) {
|
||||
ret = ipwireless_set_DTR(tty->hardware,
|
||||
tty->secondary_channel_idx, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ipw_tiocmget(struct tty_struct *linux_tty)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
/* FIXME: Exactly how is the tty object locked here .. */
|
||||
|
||||
if (!tty)
|
||||
return -ENODEV;
|
||||
|
||||
if (!tty->port.count)
|
||||
return -EINVAL;
|
||||
|
||||
return get_control_lines(tty);
|
||||
}
|
||||
|
||||
static int
|
||||
ipw_tiocmset(struct tty_struct *linux_tty,
|
||||
unsigned int set, unsigned int clear)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
/* FIXME: Exactly how is the tty object locked here .. */
|
||||
|
||||
if (!tty)
|
||||
return -ENODEV;
|
||||
|
||||
if (!tty->port.count)
|
||||
return -EINVAL;
|
||||
|
||||
return set_control_lines(tty, set, clear);
|
||||
}
|
||||
|
||||
static int ipw_ioctl(struct tty_struct *linux_tty,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct ipw_tty *tty = linux_tty->driver_data;
|
||||
|
||||
if (!tty)
|
||||
return -ENODEV;
|
||||
|
||||
if (!tty->port.count)
|
||||
return -EINVAL;
|
||||
|
||||
/* FIXME: Exactly how is the tty object locked here .. */
|
||||
if (tty->tty_type == TTYTYPE_MODEM) {
|
||||
switch (cmd) {
|
||||
case PPPIOCGCHAN:
|
||||
{
|
||||
int chan = ipwireless_ppp_channel_index(
|
||||
tty->network);
|
||||
|
||||
if (chan < 0)
|
||||
return -ENODEV;
|
||||
if (put_user(chan, (int __user *) arg))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case PPPIOCGUNIT:
|
||||
{
|
||||
int unit = ipwireless_ppp_unit_number(
|
||||
tty->network);
|
||||
|
||||
if (unit < 0)
|
||||
return -ENODEV;
|
||||
if (put_user(unit, (int __user *) arg))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
|
||||
case FIONREAD:
|
||||
{
|
||||
int val = 0;
|
||||
|
||||
if (put_user(val, (int __user *) arg))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
case TCFLSH:
|
||||
return tty_perform_flush(linux_tty, arg);
|
||||
}
|
||||
}
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static int add_tty(int j,
|
||||
struct ipw_hardware *hardware,
|
||||
struct ipw_network *network, int channel_idx,
|
||||
int secondary_channel_idx, int tty_type)
|
||||
{
|
||||
ttys[j] = kzalloc_obj(struct ipw_tty);
|
||||
if (!ttys[j])
|
||||
return -ENOMEM;
|
||||
ttys[j]->index = j;
|
||||
ttys[j]->hardware = hardware;
|
||||
ttys[j]->channel_idx = channel_idx;
|
||||
ttys[j]->secondary_channel_idx = secondary_channel_idx;
|
||||
ttys[j]->network = network;
|
||||
ttys[j]->tty_type = tty_type;
|
||||
mutex_init(&ttys[j]->ipw_tty_mutex);
|
||||
tty_port_init(&ttys[j]->port);
|
||||
|
||||
tty_port_register_device(&ttys[j]->port, ipw_tty_driver, j, NULL);
|
||||
ipwireless_associate_network_tty(network, channel_idx, ttys[j]);
|
||||
|
||||
if (secondary_channel_idx != -1)
|
||||
ipwireless_associate_network_tty(network,
|
||||
secondary_channel_idx,
|
||||
ttys[j]);
|
||||
/* check if we provide raw device (if loopback is enabled) */
|
||||
if (get_tty(j))
|
||||
printk(KERN_INFO IPWIRELESS_PCCARD_NAME
|
||||
": registering %s device ttyIPWp%d\n",
|
||||
tty_type_name(tty_type), j);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hardware,
|
||||
struct ipw_network *network)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
for (i = 0; i < IPWIRELESS_PCMCIA_MINOR_RANGE; i++) {
|
||||
int allfree = 1;
|
||||
|
||||
for (j = i; j < IPWIRELESS_PCMCIA_MINORS;
|
||||
j += IPWIRELESS_PCMCIA_MINOR_RANGE)
|
||||
if (ttys[j] != NULL) {
|
||||
allfree = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (allfree) {
|
||||
j = i;
|
||||
|
||||
if (add_tty(j, hardware, network,
|
||||
IPW_CHANNEL_DIALLER, IPW_CHANNEL_RAS,
|
||||
TTYTYPE_MODEM))
|
||||
return NULL;
|
||||
|
||||
j += IPWIRELESS_PCMCIA_MINOR_RANGE;
|
||||
if (add_tty(j, hardware, network,
|
||||
IPW_CHANNEL_DIALLER, -1,
|
||||
TTYTYPE_MONITOR))
|
||||
return NULL;
|
||||
|
||||
j += IPWIRELESS_PCMCIA_MINOR_RANGE;
|
||||
if (add_tty(j, hardware, network,
|
||||
IPW_CHANNEL_RAS, -1,
|
||||
TTYTYPE_RAS_RAW))
|
||||
return NULL;
|
||||
|
||||
return ttys[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be called before ipwireless_network_free().
|
||||
*/
|
||||
void ipwireless_tty_free(struct ipw_tty *tty)
|
||||
{
|
||||
int j;
|
||||
struct ipw_network *network = ttys[tty->index]->network;
|
||||
|
||||
for (j = tty->index; j < IPWIRELESS_PCMCIA_MINORS;
|
||||
j += IPWIRELESS_PCMCIA_MINOR_RANGE) {
|
||||
struct ipw_tty *ttyj = ttys[j];
|
||||
|
||||
if (ttyj) {
|
||||
mutex_lock(&ttyj->ipw_tty_mutex);
|
||||
if (get_tty(j))
|
||||
printk(KERN_INFO IPWIRELESS_PCCARD_NAME
|
||||
": deregistering %s device ttyIPWp%d\n",
|
||||
tty_type_name(ttyj->tty_type), j);
|
||||
if (ttyj->port.tty != NULL) {
|
||||
mutex_unlock(&ttyj->ipw_tty_mutex);
|
||||
tty_vhangup(ttyj->port.tty);
|
||||
/* FIXME: Exactly how is the tty object locked here
|
||||
against a parallel ioctl etc */
|
||||
/* FIXME2: hangup does not mean all processes
|
||||
* are gone */
|
||||
mutex_lock(&ttyj->ipw_tty_mutex);
|
||||
}
|
||||
while (ttyj->port.count)
|
||||
do_ipw_close(ttyj);
|
||||
ipwireless_disassociate_network_ttys(network,
|
||||
ttyj->channel_idx);
|
||||
tty_unregister_device(ipw_tty_driver, j);
|
||||
tty_port_destroy(&ttyj->port);
|
||||
ttys[j] = NULL;
|
||||
mutex_unlock(&ttyj->ipw_tty_mutex);
|
||||
kfree(ttyj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const struct tty_operations tty_ops = {
|
||||
.open = ipw_open,
|
||||
.close = ipw_close,
|
||||
.hangup = ipw_hangup,
|
||||
.write = ipw_write,
|
||||
.write_room = ipw_write_room,
|
||||
.ioctl = ipw_ioctl,
|
||||
.chars_in_buffer = ipw_chars_in_buffer,
|
||||
.tiocmget = ipw_tiocmget,
|
||||
.tiocmset = ipw_tiocmset,
|
||||
.set_serial = ipwireless_set_serial_info,
|
||||
.get_serial = ipwireless_get_serial_info,
|
||||
};
|
||||
|
||||
int ipwireless_tty_init(void)
|
||||
{
|
||||
int result;
|
||||
|
||||
ipw_tty_driver = tty_alloc_driver(IPWIRELESS_PCMCIA_MINORS,
|
||||
TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV);
|
||||
if (IS_ERR(ipw_tty_driver))
|
||||
return PTR_ERR(ipw_tty_driver);
|
||||
|
||||
ipw_tty_driver->driver_name = IPWIRELESS_PCCARD_NAME;
|
||||
ipw_tty_driver->name = "ttyIPWp";
|
||||
ipw_tty_driver->major = 0;
|
||||
ipw_tty_driver->minor_start = IPWIRELESS_PCMCIA_START;
|
||||
ipw_tty_driver->type = TTY_DRIVER_TYPE_SERIAL;
|
||||
ipw_tty_driver->subtype = SERIAL_TYPE_NORMAL;
|
||||
ipw_tty_driver->init_termios = tty_std_termios;
|
||||
ipw_tty_driver->init_termios.c_cflag =
|
||||
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
|
||||
ipw_tty_driver->init_termios.c_ispeed = 9600;
|
||||
ipw_tty_driver->init_termios.c_ospeed = 9600;
|
||||
tty_set_operations(ipw_tty_driver, &tty_ops);
|
||||
result = tty_register_driver(ipw_tty_driver);
|
||||
if (result) {
|
||||
printk(KERN_ERR IPWIRELESS_PCCARD_NAME
|
||||
": failed to register tty driver\n");
|
||||
tty_driver_kref_put(ipw_tty_driver);
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ipwireless_tty_release(void)
|
||||
{
|
||||
tty_unregister_driver(ipw_tty_driver);
|
||||
tty_driver_kref_put(ipw_tty_driver);
|
||||
}
|
||||
|
||||
int ipwireless_tty_is_modem(struct ipw_tty *tty)
|
||||
{
|
||||
return tty->tty_type == TTYTYPE_MODEM;
|
||||
}
|
||||
|
||||
void
|
||||
ipwireless_tty_notify_control_line_change(struct ipw_tty *tty,
|
||||
unsigned int channel_idx,
|
||||
unsigned int control_lines,
|
||||
unsigned int changed_mask)
|
||||
{
|
||||
unsigned int old_control_lines = tty->control_lines;
|
||||
|
||||
tty->control_lines = (tty->control_lines & ~changed_mask)
|
||||
| (control_lines & changed_mask);
|
||||
|
||||
/*
|
||||
* If DCD is de-asserted, we close the tty so pppd can tell that we
|
||||
* have gone offline.
|
||||
*/
|
||||
if ((old_control_lines & IPW_CONTROL_LINE_DCD)
|
||||
&& !(tty->control_lines & IPW_CONTROL_LINE_DCD)
|
||||
&& tty->port.tty) {
|
||||
tty_hangup(tty->port.tty);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* IPWireless 3G PCMCIA Network Driver
|
||||
*
|
||||
* Original code
|
||||
* by Stephen Blackheath <stephen@blacksapphire.com>,
|
||||
* Ben Martel <benm@symmetric.co.nz>
|
||||
*
|
||||
* Copyrighted as follows:
|
||||
* Copyright (C) 2004 by Symmetric Systems Ltd (NZ)
|
||||
*
|
||||
* Various driver changes and rewrites, port to new kernels
|
||||
* Copyright (C) 2006-2007 Jiri Kosina
|
||||
*
|
||||
* Misc code cleanups and updates
|
||||
* Copyright (C) 2007 David Sterba
|
||||
*/
|
||||
|
||||
#ifndef _IPWIRELESS_CS_TTY_H_
|
||||
#define _IPWIRELESS_CS_TTY_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
|
||||
#include <pcmcia/cistpl.h>
|
||||
#include <pcmcia/ds.h>
|
||||
|
||||
struct ipw_tty;
|
||||
struct ipw_network;
|
||||
struct ipw_hardware;
|
||||
|
||||
int ipwireless_tty_init(void);
|
||||
void ipwireless_tty_release(void);
|
||||
|
||||
struct ipw_tty *ipwireless_tty_create(struct ipw_hardware *hw,
|
||||
struct ipw_network *net);
|
||||
void ipwireless_tty_free(struct ipw_tty *tty);
|
||||
void ipwireless_tty_received(struct ipw_tty *tty, unsigned char *data,
|
||||
unsigned int length);
|
||||
int ipwireless_tty_is_modem(struct ipw_tty *tty);
|
||||
void ipwireless_tty_notify_control_line_change(struct ipw_tty *tty,
|
||||
unsigned int channel_idx,
|
||||
unsigned int control_lines,
|
||||
unsigned int changed_mask);
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user