// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * nozomi.c -- HSDPA driver Broadband Wireless Data Card - Globe Trotter * * Written by: Ulf Jakobsson, * Jan Åkerfeldt, * Stefan Thomasson, * * Maintained by: Paul Hardwick (p.hardwick@option.com) * * Patches: * Locking code changes for Vodafone by Sphere Systems Ltd, * Andrew Bird (ajb@spheresystems.co.uk ) * & Phil Sanderson * * Source has been ported from an implementation made by Filip Aben @ Option * * -------------------------------------------------------------------------- * * Copyright (c) 2005,2006 Option Wireless Sweden AB * Copyright (c) 2006 Sphere Systems Ltd * Copyright (c) 2006 Option Wireless n/v * All rights Reserved. * * --------------------------------------------------------------------------
*/
/* Enable this to have a lot of debug printouts */ #define DEBUG
/* * There are two types of nozomi cards, * one with 2048 memory and with 8192 memory
*/ enum card_type {
F32_2 = 2048, /* 512 bytes downlink + uplink * 2 -> 2048 */
F32_8 = 8192, /* 3072 bytes downl. + 1024 bytes uplink * 2 -> 8192 */
};
/* Initialization states a card can be in */ enum card_state {
NOZOMI_STATE_UNKNOWN = 0,
NOZOMI_STATE_ENABLED = 1, /* pci device enabled */
NOZOMI_STATE_ALLOCATED = 2, /* config setup done */
NOZOMI_STATE_READY = 3, /* flowcontrols received */
};
/* Two different toggle channels exist */ enum channel_type {
CH_A = 0,
CH_B = 1,
};
/* Port definition for the card regarding flow control */ enum ctrl_port_type {
CTRL_CMD = 0,
CTRL_MDM = 1,
CTRL_DIAG = 2,
CTRL_APP1 = 3,
CTRL_APP2 = 4,
CTRL_ERROR = -1,
};
/* Ports that the nozomi has */ enum port_type {
PORT_MDM = 0,
PORT_DIAG = 1,
PORT_APP1 = 2,
PORT_APP2 = 3,
PORT_CTRL = 4,
PORT_ERROR = -1,
};
#ifdef __BIG_ENDIAN /* Big endian */
struct toggles { unsignedint enabled:5; /* * Toggle fields are valid if enabled is 0, * else A-channels must always be used.
*/ unsignedint diag_dl:1; unsignedint mdm_dl:1; unsignedint mdm_ul:1;
} __attribute__ ((packed));
/* Configuration table to read at startup of card */ /* Is for now only needed during initialization phase */ struct config_table {
u32 signature;
u16 product_information;
u16 version;
u8 pad3[3]; struct toggles toggle;
u8 pad1[4];
u16 dl_mdm_len1; /* * If this is 64, it can hold * 60 bytes + 4 that is length field
*/
u16 dl_start;
u16 dl_diag_len1;
u16 dl_mdm_len2; /* * If this is 64, it can hold * 60 bytes + 4 that is length field
*/
u16 dl_app1_len;
/* This stores all control downlink flags */ struct ctrl_dl {
u8 port; unsignedint reserved:4; unsignedint CTS:1; unsignedint RI:1; unsignedint DCD:1; unsignedint DSR:1;
} __attribute__ ((packed));
/* This stores all control uplink flags */ struct ctrl_ul {
u8 port; unsignedint reserved:6; unsignedint RTS:1; unsignedint DTR:1;
} __attribute__ ((packed));
#else /* Little endian */
/* This represents the toggle information */ struct toggles { unsignedint mdm_ul:1; unsignedint mdm_dl:1; unsignedint diag_dl:1; unsignedint enabled:5; /* * Toggle fields are valid if enabled is 0, * else A-channels must always be used.
*/
} __attribute__ ((packed));
/* Configuration table to read at startup of card */ struct config_table {
u32 signature;
u16 version;
u16 product_information; struct toggles toggle;
u8 pad1[7];
u16 dl_start;
u16 dl_mdm_len1; /* * If this is 64, it can hold * 60 bytes + 4 that is length field
*/
u16 dl_mdm_len2;
u16 dl_diag_len1;
u16 dl_diag_len2;
u16 dl_app1_len;
u16 dl_app2_len;
u16 dl_ctrl_len;
u8 pad2[16];
u16 ul_start;
u16 ul_mdm_len2;
u16 ul_mdm_len1;
u16 ul_diag_len;
u16 ul_app1_len;
u16 ul_app2_len;
u16 ul_ctrl_len;
} __attribute__ ((packed));
/* This stores all control downlink flags */ struct ctrl_dl { unsignedint DSR:1; unsignedint DCD:1; unsignedint RI:1; unsignedint CTS:1; unsignedint reserved:4;
u8 port;
} __attribute__ ((packed));
/* This stores all control uplink flags */ struct ctrl_ul { unsignedint DTR:1; unsignedint RTS:1; unsignedint reserved:6;
u8 port;
} __attribute__ ((packed)); #endif
/* This holds all information that is needed regarding a port */ struct port { struct tty_port port;
u8 update_flow_control; struct ctrl_ul ctrl_ul; struct ctrl_dl ctrl_dl; struct kfifo fifo_ul; void __iomem *dl_addr[2];
u32 dl_size[2];
u8 toggle_dl; void __iomem *ul_addr[2];
u32 ul_size[2];
u8 toggle_ul;
u16 token_dl;
/* shortcut for extremely often used cases */ switch (size_bytes) { case 2: /* 2 bytes */
buf16 = (const u16 *)buf;
writew(__cpu_to_le16(*buf16), ptr); return 2; case 1: /* * also needs to write 4 bytes in this case * so falling through..
*/
fallthrough; case 4: /* 4 bytes */
writel(__cpu_to_le32(*buf), ptr); return 4;
}
while (i < size_bytes) { if (size_bytes - i == 2) { /* 2 bytes */
buf16 = (const u16 *)buf;
writew(__cpu_to_le16(*buf16), ptr);
i += 2;
} else { /* 4 bytes */
writel(__cpu_to_le32(*buf), ptr);
i += 4;
}
buf++;
ptr++;
} return i;
}
/* Setup pointers to different channels and also setup buffer sizes. */ staticvoid nozomi_setup_memory(struct nozomi *dc)
{ void __iomem *offset = dc->base_addr + dc->config_table.dl_start; /* The length reported is including the length field of 4 bytes, * hence subtract with 4.
*/ const u16 buff_offset = 4;
switch (ctrl_dl.port) { case CTRL_CMD:
DBG1("The Base Band sends this value as a response to a " "request for IMSI detach sent over the control " "channel uplink (see section 7.6.1)."); break; case CTRL_MDM:
port = PORT_MDM;
enable_ier = MDM_DL; break; case CTRL_DIAG:
port = PORT_DIAG;
enable_ier = DIAG_DL; break; case CTRL_APP1:
port = PORT_APP1;
enable_ier = APP1_DL; break; case CTRL_APP2:
port = PORT_APP2;
enable_ier = APP2_DL; if (dc->state == NOZOMI_STATE_ALLOCATED) { /* * After card initialization the flow control * received for APP2 is always the last
*/
dc->state = NOZOMI_STATE_READY;
dev_info(&dc->pdev->dev, "Device READY!\n");
} break; default:
dev_err(&dc->pdev->dev, "ERROR: flow control received for non-existing port\n"); return 0;
}
staticenum ctrl_port_type port2ctrl(enum port_type port, conststruct nozomi *dc)
{ switch (port) { case PORT_MDM: return CTRL_MDM; case PORT_DIAG: return CTRL_DIAG; case PORT_APP1: return CTRL_APP1; case PORT_APP2: return CTRL_APP2; default:
dev_err(&dc->pdev->dev, "ERROR: send flow control " \ "received for non-existing port\n");
} return CTRL_ERROR;
}
/* * Send flow control, can only update one channel at a time * Return 0 - If we have updated all flow control * Return 1 - If we need to update more flow control, ack current enable more
*/ staticint send_flow_control(struct nozomi *dc)
{
u32 i, more_flow_control_to_be_updated = 0;
u16 *ctrl;
for (i = PORT_MDM; i < MAX_PORT; i++) { if (dc->port[i].update_flow_control) { if (more_flow_control_to_be_updated) { /* We have more flow control to be updated */ return 1;
}
dc->port[i].ctrl_ul.port = port2ctrl(i, dc);
ctrl = (u16 *)&dc->port[i].ctrl_ul;
write_mem32(dc->port[PORT_CTRL].ul_addr[0], \
(u32 *) ctrl, 2);
dc->port[i].update_flow_control = 0;
more_flow_control_to_be_updated = 1;
}
} return 0;
}
/* * Handle downlink data, ports that are handled are modem and diagnostics * Return 1 - ok * Return 0 - toggle fields are out of sync
*/ staticint handle_data_dl(struct nozomi *dc, enum port_type port, u8 *toggle,
u16 read_iir, u16 mask1, u16 mask2)
{ if (*toggle == 0 && read_iir & mask1) { if (receive_data(port, dc)) {
writew(mask1, dc->reg_fcr);
*toggle = !(*toggle);
}
if (read_iir & mask1) { if (receive_data(port, dc)) {
writew(mask1, dc->reg_fcr);
*toggle = !(*toggle);
}
}
} else {
dev_err(&dc->pdev->dev, "port out of sync!, toggle:%d\n",
*toggle); return 0;
} return 1;
}
/* * Handle uplink data, this is currently for the modem port * Return 1 - ok * Return 0 - toggle field are out of sync
*/ staticint handle_data_ul(struct nozomi *dc, enum port_type port, u16 read_iir)
{
u8 *toggle = &(dc->port[port].toggle_ul);
staticvoid make_sysfs_files(struct nozomi *dc)
{ if (device_create_file(&dc->pdev->dev, &dev_attr_card_type))
dev_err(&dc->pdev->dev, "Could not create sysfs file for card_type\n"); if (device_create_file(&dc->pdev->dev, &dev_attr_open_ttys))
dev_err(&dc->pdev->dev, "Could not create sysfs file for open_ttys\n");
}
/* Allocate memory for one device */ staticint nozomi_card_init(struct pci_dev *pdev, conststruct pci_device_id *ent)
{ int ret; struct nozomi *dc = NULL; int ndev_idx; int i;
for (ndev_idx = 0; ndev_idx < ARRAY_SIZE(ndevs); ndev_idx++) if (!ndevs[ndev_idx]) break;
if (ndev_idx >= ARRAY_SIZE(ndevs)) {
dev_err(&pdev->dev, "no free tty range for this card left\n");
ret = -EIO; goto err;
}
dc = kzalloc(sizeof(struct nozomi), GFP_KERNEL); if (unlikely(!dc)) {
dev_err(&pdev->dev, "Could not allocate memory\n");
ret = -ENOMEM; goto err_free;
}
dc->pdev = pdev;
ret = pci_enable_device(dc->pdev); if (ret) {
dev_err(&pdev->dev, "Failed to enable PCI Device\n"); goto err_free;
}
ret = pci_request_regions(dc->pdev, NOZOMI_NAME); if (ret) {
dev_err(&pdev->dev, "I/O address 0x%04x already in use\n",
(int) /* nozomi_private.io_addr */ 0); goto err_disable_device;
}
/* Find out what card type it is */
nozomi_get_card_type(dc);
dc->base_addr = pci_iomap(dc->pdev, 0, dc->card_type); if (!dc->base_addr) {
dev_err(&pdev->dev, "Unable to map card MMIO\n");
ret = -ENODEV; goto err_rel_regs;
}
dc->send_buf = kmalloc(SEND_BUF_MAX, GFP_KERNEL); if (!dc->send_buf) {
dev_err(&pdev->dev, "Could not allocate send buffer?\n");
ret = -ENOMEM; goto err_free_sbuf;
}
for (i = PORT_MDM; i < MAX_PORT; i++) { if (kfifo_alloc(&dc->port[i].fifo_ul, FIFO_BUFFER_SIZE_UL,
GFP_KERNEL)) {
dev_err(&pdev->dev, "Could not allocate kfifo buffer\n");
ret = -ENOMEM; goto err_free_kfifo;
}
}
spin_lock_init(&dc->spin_mutex);
nozomi_setup_private_data(dc);
/* Disable all interrupts */
dc->last_ier = 0;
writew(dc->last_ier, dc->reg_ier);
ret = request_irq(pdev->irq, &interrupt_handler, IRQF_SHARED,
NOZOMI_NAME, dc); if (unlikely(ret)) {
dev_err(&pdev->dev, "can't request irq %d\n", pdev->irq); goto err_free_all_kfifo;
}
for (i = 0; i < MAX_PORT; ++i)
tty_port_tty_hangup(&dc->port[i].port, false);
/* Racy below - surely should wait for scheduled work to be done or
complete off a hangup method ? */ while (dc->open_ttys)
msleep(1); for (i = 0; i < MAX_PORT; ++i) {
tty_unregister_device(ntty_driver, dc->index_start + i);
tty_port_destroy(&dc->port[i].port);
}
}
/* Deallocate memory for one device */ staticvoid nozomi_card_exit(struct pci_dev *pdev)
{ int i; struct ctrl_ul ctrl; struct nozomi *dc = pci_get_drvdata(pdev);
/* Disable all interrupts */
dc->last_ier = 0;
writew(dc->last_ier, dc->reg_ier);
tty_exit(dc);
/* Send 0x0001, command card to resend the reset token. */ /* This is to get the reset when the module is reloaded. */
ctrl.port = 0x00;
ctrl.reserved = 0;
ctrl.RTS = 0;
ctrl.DTR = 1;
DBG1("sending flow control 0x%04X", *((u16 *)&ctrl));
/* Setup dc->reg addresses to we can use defines here */
write_mem32(dc->port[PORT_CTRL].ul_addr[0], (u32 *)&ctrl, 2);
writew(CTRL_UL, dc->reg_fcr); /* push the token to the card. */
remove_sysfs_files(dc);
free_irq(pdev->irq, dc);
for (i = 0; i < MAX_PORT; i++)
kfifo_free(&dc->port[i].fifo_ul);
kfree(dc->send_buf);
iounmap(dc->base_addr);
pci_release_regions(pdev);
pci_disable_device(pdev);
ndevs[dc->index_start / MAX_PORT] = NULL;
kfree(dc);
}
staticvoid set_rts(conststruct tty_struct *tty, int rts)
{ struct port *port = get_port_by_tty(tty);
/* * called when the userspace process writes to the tty (/dev/noz*). * Data is inserted into a fifo, which is then read and transferred to the modem.
*/ static ssize_t ntty_write(struct tty_struct *tty, const u8 *buffer,
size_t count)
{ struct nozomi *dc = get_dc_by_tty(tty); struct port *port = tty->driver_data; unsignedlong flags;
size_t rval;
if (!dc || !port) return -ENODEV;
rval = kfifo_in(&port->fifo_ul, buffer, count);
spin_lock_irqsave(&dc->spin_mutex, flags); /* CTS is only valid on the modem channel */ if (port == &(dc->port[PORT_MDM])) { if (port->ctrl_dl.CTS) {
DBG4("Enable interrupt");
enable_transmit_ul(tty->index % MAX_PORT, dc);
} else {
dev_err(&dc->pdev->dev, "CTS not active on modem port?\n");
}
} else {
enable_transmit_ul(tty->index % MAX_PORT, dc);
}
spin_unlock_irqrestore(&dc->spin_mutex, flags);
return rval;
}
/* * Calculate how much is left in device * This method is called by the upper tty layer. * #according to sources N_TTY.c it expects a value >= 0 and * does not check for negative values. * * If the port is unplugged report lots of room and let the bits * dribble away so we don't block anything.
*/ staticunsignedint ntty_write_room(struct tty_struct *tty)
{ struct port *port = tty->driver_data; unsignedint room = 4096; conststruct nozomi *dc = get_dc_by_tty(tty);
/* Note: these could change under us but it is not clear this
matters if so */ return (ctrl_ul->RTS ? TIOCM_RTS : 0)
| (ctrl_ul->DTR ? TIOCM_DTR : 0)
| (ctrl_dl->DCD ? TIOCM_CAR : 0)
| (ctrl_dl->RI ? TIOCM_RNG : 0)
| (ctrl_dl->DSR ? TIOCM_DSR : 0)
| (ctrl_dl->CTS ? TIOCM_CTS : 0);
}
/* * Called by the upper tty layer when tty buffers are ready * to receive data again after a call to throttle.
*/ staticvoid ntty_unthrottle(struct tty_struct *tty)
{ struct nozomi *dc = get_dc_by_tty(tty); unsignedlong flags;
/* * Called by the upper tty layer when the tty buffers are almost full. * The driver should stop send more data.
*/ staticvoid ntty_throttle(struct tty_struct *tty)
{ struct nozomi *dc = get_dc_by_tty(tty); unsignedlong flags;
/* Returns number of chars in buffer, called by tty layer */ staticunsignedint ntty_chars_in_buffer(struct tty_struct *tty)
{ struct port *port = tty->driver_data; struct nozomi *dc = get_dc_by_tty(tty);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.