Staging: gdm72xx: Remove gdm72xx driver
Remove support for gdm72xx driver from the kernel since Wimax is dead. [1] http://www.networkworld.com/article/2220370/4g/wimax-is-dead.html [2] http://www.androidcentral.com/sprint-confirms-wimax-shutdown-november-6-2015 Chrome OS can distribute this driver alongside their library. Signed-off-by: Shraddha Barke <shraddha.6596@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
e0a2d503a9
Коммит
3765dc92cb
|
@ -72,8 +72,6 @@ source "drivers/staging/android/Kconfig"
|
|||
|
||||
source "drivers/staging/board/Kconfig"
|
||||
|
||||
source "drivers/staging/gdm72xx/Kconfig"
|
||||
|
||||
source "drivers/staging/gdm724x/Kconfig"
|
||||
|
||||
source "drivers/staging/fwserial/Kconfig"
|
||||
|
|
|
@ -26,7 +26,6 @@ obj-$(CONFIG_MFD_NVEC) += nvec/
|
|||
obj-$(CONFIG_STAGING_RDMA) += rdma/
|
||||
obj-$(CONFIG_ANDROID) += android/
|
||||
obj-$(CONFIG_STAGING_BOARD) += board/
|
||||
obj-$(CONFIG_WIMAX_GDM72XX) += gdm72xx/
|
||||
obj-$(CONFIG_LTE_GDM724X) += gdm724x/
|
||||
obj-$(CONFIG_FIREWIRE_SERIAL) += fwserial/
|
||||
obj-$(CONFIG_GOLDFISH) += goldfish/
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
#
|
||||
# GCT GDM72xx WiMAX driver configuration
|
||||
#
|
||||
|
||||
menuconfig WIMAX_GDM72XX
|
||||
tristate "GCT GDM72xx WiMAX support"
|
||||
depends on NET && (USB || MMC)
|
||||
help
|
||||
Support a WiMAX module based on the GCT GDM72xx WiMAX chip.
|
||||
|
||||
if WIMAX_GDM72XX
|
||||
|
||||
config WIMAX_GDM72XX_QOS
|
||||
bool "Enable QoS support"
|
||||
default n
|
||||
help
|
||||
Enable Quality of Service support based on the data protocol of
|
||||
transmitting packets.
|
||||
|
||||
config WIMAX_GDM72XX_K_MODE
|
||||
bool "Enable K mode"
|
||||
default n
|
||||
help
|
||||
Enable support for proprietary functions for KT (Korea Telecom).
|
||||
|
||||
config WIMAX_GDM72XX_WIMAX2
|
||||
bool "Enable WiMAX2 support"
|
||||
default n
|
||||
help
|
||||
Enable support for transmitting multiple packets (packet
|
||||
aggregation) from the WiMAX module to the host processor.
|
||||
|
||||
choice
|
||||
prompt "Select interface"
|
||||
|
||||
config WIMAX_GDM72XX_USB
|
||||
bool "USB interface"
|
||||
depends on (USB = y || USB = WIMAX_GDM72XX)
|
||||
help
|
||||
Select this option if the WiMAX module interfaces with the host
|
||||
processor via USB.
|
||||
|
||||
config WIMAX_GDM72XX_SDIO
|
||||
bool "SDIO interface"
|
||||
depends on (MMC = y || MMC = WIMAX_GDM72XX)
|
||||
help
|
||||
Select this option if the WiMAX module interfaces with the host
|
||||
processor via SDIO.
|
||||
|
||||
endchoice
|
||||
|
||||
if WIMAX_GDM72XX_USB
|
||||
|
||||
config WIMAX_GDM72XX_USB_PM
|
||||
bool "Enable power management support"
|
||||
depends on PM
|
||||
help
|
||||
Enable USB power management in order to reduce power consumption
|
||||
while the interface is not in use.
|
||||
|
||||
endif # WIMAX_GDM72XX_USB
|
||||
|
||||
endif # WIMAX_GDM72XX
|
|
@ -1,6 +0,0 @@
|
|||
obj-$(CONFIG_WIMAX_GDM72XX) := gdmwm.o
|
||||
|
||||
gdmwm-y += gdm_wimax.o netlink_k.o
|
||||
gdmwm-$(CONFIG_WIMAX_GDM72XX_QOS) += gdm_qos.o
|
||||
gdmwm-$(CONFIG_WIMAX_GDM72XX_SDIO) += gdm_sdio.o sdio_boot.o
|
||||
gdmwm-$(CONFIG_WIMAX_GDM72XX_USB) += gdm_usb.o usb_boot.o
|
|
@ -1,2 +0,0 @@
|
|||
TODO:
|
||||
- Clean up coding style to meet kernel standard.
|
|
@ -1,438 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include <linux/ip.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/if_ether.h>
|
||||
|
||||
#include "gdm_wimax.h"
|
||||
#include "hci.h"
|
||||
#include "gdm_qos.h"
|
||||
|
||||
#define MAX_FREE_LIST_CNT 32
|
||||
static struct {
|
||||
struct list_head head;
|
||||
int cnt;
|
||||
spinlock_t lock; /* protect structure fields */
|
||||
} qos_free_list;
|
||||
|
||||
static void init_qos_entry_list(void)
|
||||
{
|
||||
qos_free_list.cnt = 0;
|
||||
INIT_LIST_HEAD(&qos_free_list.head);
|
||||
spin_lock_init(&qos_free_list.lock);
|
||||
}
|
||||
|
||||
static void *alloc_qos_entry(void)
|
||||
{
|
||||
struct qos_entry_s *entry;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&qos_free_list.lock, flags);
|
||||
if (qos_free_list.cnt) {
|
||||
entry = list_entry(qos_free_list.head.prev, struct qos_entry_s,
|
||||
list);
|
||||
list_del(&entry->list);
|
||||
qos_free_list.cnt--;
|
||||
spin_unlock_irqrestore(&qos_free_list.lock, flags);
|
||||
return entry;
|
||||
}
|
||||
spin_unlock_irqrestore(&qos_free_list.lock, flags);
|
||||
|
||||
return kmalloc(sizeof(*entry), GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static void free_qos_entry(void *entry)
|
||||
{
|
||||
struct qos_entry_s *qentry = entry;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&qos_free_list.lock, flags);
|
||||
if (qos_free_list.cnt < MAX_FREE_LIST_CNT) {
|
||||
list_add(&qentry->list, &qos_free_list.head);
|
||||
qos_free_list.cnt++;
|
||||
spin_unlock_irqrestore(&qos_free_list.lock, flags);
|
||||
return;
|
||||
}
|
||||
spin_unlock_irqrestore(&qos_free_list.lock, flags);
|
||||
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
static void free_qos_entry_list(struct list_head *free_list)
|
||||
{
|
||||
struct qos_entry_s *entry, *n;
|
||||
int total_free = 0;
|
||||
|
||||
list_for_each_entry_safe(entry, n, free_list, list) {
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
total_free++;
|
||||
}
|
||||
|
||||
pr_debug("%s: total_free_cnt=%d\n", __func__, total_free);
|
||||
}
|
||||
|
||||
void gdm_qos_init(void *nic_ptr)
|
||||
{
|
||||
struct nic *nic = nic_ptr;
|
||||
struct qos_cb_s *qcb = &nic->qos;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < QOS_MAX; i++) {
|
||||
INIT_LIST_HEAD(&qcb->qos_list[i]);
|
||||
qcb->csr[i].qos_buf_count = 0;
|
||||
qcb->csr[i].enabled = false;
|
||||
}
|
||||
|
||||
qcb->qos_list_cnt = 0;
|
||||
qcb->qos_null_idx = QOS_MAX - 1;
|
||||
qcb->qos_limit_size = 255;
|
||||
|
||||
spin_lock_init(&qcb->qos_lock);
|
||||
|
||||
init_qos_entry_list();
|
||||
}
|
||||
|
||||
void gdm_qos_release_list(void *nic_ptr)
|
||||
{
|
||||
struct nic *nic = nic_ptr;
|
||||
struct qos_cb_s *qcb = &nic->qos;
|
||||
unsigned long flags;
|
||||
struct qos_entry_s *entry, *n;
|
||||
struct list_head free_list;
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(&free_list);
|
||||
|
||||
spin_lock_irqsave(&qcb->qos_lock, flags);
|
||||
|
||||
for (i = 0; i < QOS_MAX; i++) {
|
||||
qcb->csr[i].qos_buf_count = 0;
|
||||
qcb->csr[i].enabled = false;
|
||||
}
|
||||
|
||||
qcb->qos_list_cnt = 0;
|
||||
qcb->qos_null_idx = QOS_MAX - 1;
|
||||
|
||||
for (i = 0; i < QOS_MAX; i++) {
|
||||
list_for_each_entry_safe(entry, n, &qcb->qos_list[i], list) {
|
||||
list_move_tail(&entry->list, &free_list);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&qcb->qos_lock, flags);
|
||||
free_qos_entry_list(&free_list);
|
||||
}
|
||||
|
||||
static int chk_ipv4_rule(struct gdm_wimax_csr_s *csr, u8 *stream, u8 *port)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (csr->classifier_rule_en & IPTYPEOFSERVICE) {
|
||||
if (((stream[1] & csr->ip2s_mask) < csr->ip2s_lo) ||
|
||||
((stream[1] & csr->ip2s_mask) > csr->ip2s_hi))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (csr->classifier_rule_en & PROTOCOL) {
|
||||
if (stream[9] != csr->protocol)
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (csr->classifier_rule_en & IPMASKEDSRCADDRESS) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
if ((stream[12 + i] & csr->ipsrc_addrmask[i]) !=
|
||||
(csr->ipsrc_addr[i] & csr->ipsrc_addrmask[i]))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (csr->classifier_rule_en & IPMASKEDDSTADDRESS) {
|
||||
for (i = 0; i < 4; i++) {
|
||||
if ((stream[16 + i] & csr->ipdst_addrmask[i]) !=
|
||||
(csr->ipdst_addr[i] & csr->ipdst_addrmask[i]))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (csr->classifier_rule_en & PROTOCOLSRCPORTRANGE) {
|
||||
i = ((port[0] << 8) & 0xff00) + port[1];
|
||||
if ((i < csr->srcport_lo) || (i > csr->srcport_hi))
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (csr->classifier_rule_en & PROTOCOLDSTPORTRANGE) {
|
||||
i = ((port[2] << 8) & 0xff00) + port[3];
|
||||
if ((i < csr->dstport_lo) || (i > csr->dstport_hi))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_qos_index(struct nic *nic, u8 *iph, u8 *tcpudph)
|
||||
{
|
||||
int ip_ver, i;
|
||||
struct qos_cb_s *qcb = &nic->qos;
|
||||
|
||||
if (!iph || !tcpudph)
|
||||
return -1;
|
||||
|
||||
ip_ver = (iph[0] >> 4) & 0xf;
|
||||
|
||||
if (ip_ver != 4)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < QOS_MAX; i++) {
|
||||
if (!qcb->csr[i].enabled)
|
||||
continue;
|
||||
if (!qcb->csr[i].classifier_rule_en)
|
||||
continue;
|
||||
if (chk_ipv4_rule(&qcb->csr[i], iph, tcpudph) == 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void extract_qos_list(struct nic *nic, struct list_head *head)
|
||||
{
|
||||
struct qos_cb_s *qcb = &nic->qos;
|
||||
struct qos_entry_s *entry;
|
||||
int i;
|
||||
|
||||
INIT_LIST_HEAD(head);
|
||||
|
||||
for (i = 0; i < QOS_MAX; i++) {
|
||||
if (!qcb->csr[i].enabled)
|
||||
continue;
|
||||
if (qcb->csr[i].qos_buf_count >= qcb->qos_limit_size)
|
||||
continue;
|
||||
if (list_empty(&qcb->qos_list[i]))
|
||||
continue;
|
||||
|
||||
entry = list_entry(qcb->qos_list[i].prev, struct qos_entry_s,
|
||||
list);
|
||||
|
||||
list_move_tail(&entry->list, head);
|
||||
qcb->csr[i].qos_buf_count++;
|
||||
|
||||
if (!list_empty(&qcb->qos_list[i]))
|
||||
netdev_warn(nic->netdev, "Index(%d) is piled!!\n", i);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_qos_list(struct nic *nic, struct list_head *head)
|
||||
{
|
||||
struct qos_entry_s *entry, *n;
|
||||
|
||||
list_for_each_entry_safe(entry, n, head, list) {
|
||||
list_del(&entry->list);
|
||||
gdm_wimax_send_tx(entry->skb, entry->dev);
|
||||
free_qos_entry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
int gdm_qos_send_hci_pkt(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
int index;
|
||||
struct qos_cb_s *qcb = &nic->qos;
|
||||
unsigned long flags;
|
||||
struct ethhdr *ethh = (struct ethhdr *)(skb->data + HCI_HEADER_SIZE);
|
||||
struct iphdr *iph = (struct iphdr *)((char *)ethh + ETH_HLEN);
|
||||
struct tcphdr *tcph;
|
||||
struct qos_entry_s *entry = NULL;
|
||||
struct list_head send_list;
|
||||
int ret = 0;
|
||||
|
||||
tcph = (struct tcphdr *)iph + iph->ihl * 4;
|
||||
|
||||
if (ethh->h_proto == cpu_to_be16(ETH_P_IP)) {
|
||||
if (qcb->qos_list_cnt && !qos_free_list.cnt) {
|
||||
entry = alloc_qos_entry();
|
||||
entry->skb = skb;
|
||||
entry->dev = dev;
|
||||
netdev_dbg(dev, "qcb->qos_list_cnt=%d\n",
|
||||
qcb->qos_list_cnt);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&qcb->qos_lock, flags);
|
||||
if (qcb->qos_list_cnt) {
|
||||
index = get_qos_index(nic, (u8 *)iph, (u8 *)tcph);
|
||||
if (index == -1)
|
||||
index = qcb->qos_null_idx;
|
||||
|
||||
if (!entry) {
|
||||
entry = alloc_qos_entry();
|
||||
entry->skb = skb;
|
||||
entry->dev = dev;
|
||||
}
|
||||
|
||||
list_add_tail(&entry->list, &qcb->qos_list[index]);
|
||||
extract_qos_list(nic, &send_list);
|
||||
spin_unlock_irqrestore(&qcb->qos_lock, flags);
|
||||
send_qos_list(nic, &send_list);
|
||||
goto out;
|
||||
}
|
||||
spin_unlock_irqrestore(&qcb->qos_lock, flags);
|
||||
if (entry)
|
||||
free_qos_entry(entry);
|
||||
}
|
||||
|
||||
ret = gdm_wimax_send_tx(skb, dev);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int get_csr(struct qos_cb_s *qcb, u32 sfid, int mode)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < qcb->qos_list_cnt; i++) {
|
||||
if (qcb->csr[i].sfid == sfid)
|
||||
return i;
|
||||
}
|
||||
|
||||
if (mode) {
|
||||
for (i = 0; i < QOS_MAX; i++) {
|
||||
if (!qcb->csr[i].enabled) {
|
||||
qcb->csr[i].enabled = true;
|
||||
qcb->qos_list_cnt++;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#define QOS_CHANGE_DEL 0xFC
|
||||
#define QOS_ADD 0xFD
|
||||
#define QOS_REPORT 0xFE
|
||||
|
||||
void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size)
|
||||
{
|
||||
struct nic *nic = nic_ptr;
|
||||
int i, index, pos;
|
||||
u32 sfid;
|
||||
u8 sub_cmd_evt;
|
||||
struct qos_cb_s *qcb = &nic->qos;
|
||||
struct qos_entry_s *entry, *n;
|
||||
struct list_head send_list;
|
||||
struct list_head free_list;
|
||||
unsigned long flags;
|
||||
|
||||
sub_cmd_evt = (u8)buf[4];
|
||||
|
||||
if (sub_cmd_evt == QOS_REPORT) {
|
||||
spin_lock_irqsave(&qcb->qos_lock, flags);
|
||||
for (i = 0; i < qcb->qos_list_cnt; i++) {
|
||||
sfid = ((buf[(i * 5) + 6] << 24) & 0xff000000);
|
||||
sfid += ((buf[(i * 5) + 7] << 16) & 0xff0000);
|
||||
sfid += ((buf[(i * 5) + 8] << 8) & 0xff00);
|
||||
sfid += (buf[(i * 5) + 9]);
|
||||
index = get_csr(qcb, sfid, 0);
|
||||
if (index == -1) {
|
||||
spin_unlock_irqrestore(&qcb->qos_lock, flags);
|
||||
netdev_err(nic->netdev, "QoS ERROR: No SF\n");
|
||||
return;
|
||||
}
|
||||
qcb->csr[index].qos_buf_count = buf[(i * 5) + 10];
|
||||
}
|
||||
|
||||
extract_qos_list(nic, &send_list);
|
||||
spin_unlock_irqrestore(&qcb->qos_lock, flags);
|
||||
send_qos_list(nic, &send_list);
|
||||
return;
|
||||
}
|
||||
|
||||
/* sub_cmd_evt == QOS_ADD || sub_cmd_evt == QOS_CHANG_DEL */
|
||||
pos = 6;
|
||||
sfid = ((buf[pos++] << 24) & 0xff000000);
|
||||
sfid += ((buf[pos++] << 16) & 0xff0000);
|
||||
sfid += ((buf[pos++] << 8) & 0xff00);
|
||||
sfid += (buf[pos++]);
|
||||
|
||||
index = get_csr(qcb, sfid, 1);
|
||||
if (index == -1) {
|
||||
netdev_err(nic->netdev,
|
||||
"QoS ERROR: csr Update Error / Wrong index (%d)\n",
|
||||
index);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sub_cmd_evt == QOS_ADD) {
|
||||
netdev_dbg(nic->netdev, "QOS_ADD SFID = 0x%x, index=%d\n",
|
||||
sfid, index);
|
||||
|
||||
spin_lock_irqsave(&qcb->qos_lock, flags);
|
||||
qcb->csr[index].sfid = sfid;
|
||||
qcb->csr[index].classifier_rule_en = ((buf[pos++] << 8) & 0xff00);
|
||||
qcb->csr[index].classifier_rule_en += buf[pos++];
|
||||
if (qcb->csr[index].classifier_rule_en == 0)
|
||||
qcb->qos_null_idx = index;
|
||||
qcb->csr[index].ip2s_mask = buf[pos++];
|
||||
qcb->csr[index].ip2s_lo = buf[pos++];
|
||||
qcb->csr[index].ip2s_hi = buf[pos++];
|
||||
qcb->csr[index].protocol = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addrmask[0] = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addrmask[1] = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addrmask[2] = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addrmask[3] = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addr[0] = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addr[1] = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addr[2] = buf[pos++];
|
||||
qcb->csr[index].ipsrc_addr[3] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addrmask[0] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addrmask[1] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addrmask[2] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addrmask[3] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addr[0] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addr[1] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addr[2] = buf[pos++];
|
||||
qcb->csr[index].ipdst_addr[3] = buf[pos++];
|
||||
qcb->csr[index].srcport_lo = ((buf[pos++] << 8) & 0xff00);
|
||||
qcb->csr[index].srcport_lo += buf[pos++];
|
||||
qcb->csr[index].srcport_hi = ((buf[pos++] << 8) & 0xff00);
|
||||
qcb->csr[index].srcport_hi += buf[pos++];
|
||||
qcb->csr[index].dstport_lo = ((buf[pos++] << 8) & 0xff00);
|
||||
qcb->csr[index].dstport_lo += buf[pos++];
|
||||
qcb->csr[index].dstport_hi = ((buf[pos++] << 8) & 0xff00);
|
||||
qcb->csr[index].dstport_hi += buf[pos++];
|
||||
|
||||
qcb->qos_limit_size = 254 / qcb->qos_list_cnt;
|
||||
spin_unlock_irqrestore(&qcb->qos_lock, flags);
|
||||
} else if (sub_cmd_evt == QOS_CHANGE_DEL) {
|
||||
netdev_dbg(nic->netdev, "QOS_CHANGE_DEL SFID = 0x%x, index=%d\n",
|
||||
sfid, index);
|
||||
|
||||
INIT_LIST_HEAD(&free_list);
|
||||
|
||||
spin_lock_irqsave(&qcb->qos_lock, flags);
|
||||
qcb->csr[index].enabled = false;
|
||||
qcb->qos_list_cnt--;
|
||||
qcb->qos_limit_size = 254 / qcb->qos_list_cnt;
|
||||
|
||||
list_for_each_entry_safe(entry, n, &qcb->qos_list[index],
|
||||
list) {
|
||||
list_move_tail(&entry->list, &free_list);
|
||||
}
|
||||
spin_unlock_irqrestore(&qcb->qos_lock, flags);
|
||||
free_qos_entry_list(&free_list);
|
||||
}
|
||||
}
|
|
@ -1,74 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_GDM_QOS_H__
|
||||
#define __GDM72XX_GDM_QOS_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#define QOS_MAX 16
|
||||
#define IPTYPEOFSERVICE 0x8000
|
||||
#define PROTOCOL 0x4000
|
||||
#define IPMASKEDSRCADDRESS 0x2000
|
||||
#define IPMASKEDDSTADDRESS 0x1000
|
||||
#define PROTOCOLSRCPORTRANGE 0x800
|
||||
#define PROTOCOLDSTPORTRANGE 0x400
|
||||
#define DSTMACADDR 0x200
|
||||
#define SRCMACADDR 0x100
|
||||
#define ETHERTYPE 0x80
|
||||
#define IEEE802_1DUSERPRIORITY 0x40
|
||||
#define IEEE802_1QVLANID 0x10
|
||||
|
||||
struct gdm_wimax_csr_s {
|
||||
bool enabled;
|
||||
u32 sfid;
|
||||
u8 qos_buf_count;
|
||||
u16 classifier_rule_en;
|
||||
u8 ip2s_lo;
|
||||
u8 ip2s_hi;
|
||||
u8 ip2s_mask;
|
||||
u8 protocol;
|
||||
u8 ipsrc_addr[16];
|
||||
u8 ipsrc_addrmask[16];
|
||||
u8 ipdst_addr[16];
|
||||
u8 ipdst_addrmask[16];
|
||||
u16 srcport_lo;
|
||||
u16 srcport_hi;
|
||||
u16 dstport_lo;
|
||||
u16 dstport_hi;
|
||||
};
|
||||
|
||||
struct qos_entry_s {
|
||||
struct list_head list;
|
||||
struct sk_buff *skb;
|
||||
struct net_device *dev;
|
||||
|
||||
};
|
||||
|
||||
struct qos_cb_s {
|
||||
struct list_head qos_list[QOS_MAX];
|
||||
int qos_list_cnt;
|
||||
int qos_null_idx;
|
||||
struct gdm_wimax_csr_s csr[QOS_MAX];
|
||||
spinlock_t qos_lock; /* Protect structure fields */
|
||||
int qos_limit_size;
|
||||
};
|
||||
|
||||
void gdm_qos_init(void *nic_ptr);
|
||||
void gdm_qos_release_list(void *nic_ptr);
|
||||
int gdm_qos_send_hci_pkt(struct sk_buff *skb, struct net_device *dev);
|
||||
void gdm_recv_qos_hci_packet(void *nic_ptr, u8 *buf, int size);
|
||||
|
||||
#endif /* __GDM72XX_GDM_QOS_H__ */
|
|
@ -1,700 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
#include <linux/mmc/sdio_ids.h>
|
||||
|
||||
#include "gdm_sdio.h"
|
||||
#include "gdm_wimax.h"
|
||||
#include "sdio_boot.h"
|
||||
#include "hci.h"
|
||||
|
||||
#define TYPE_A_HEADER_SIZE 4
|
||||
#define TYPE_A_LOOKAHEAD_SIZE 16
|
||||
|
||||
#define MAX_NR_RX_BUF 4
|
||||
|
||||
#define SDU_TX_BUF_SIZE 2048
|
||||
#define TX_BUF_SIZE 2048
|
||||
#define TX_CHUNK_SIZE (2048 - TYPE_A_HEADER_SIZE)
|
||||
#define RX_BUF_SIZE (25 * 1024)
|
||||
|
||||
#define TX_HZ 2000
|
||||
#define TX_INTERVAL (NSEC_PER_SEC / TX_HZ)
|
||||
|
||||
static struct sdio_tx *alloc_tx_struct(struct tx_cxt *tx)
|
||||
{
|
||||
struct sdio_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC);
|
||||
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC);
|
||||
if (!t->buf) {
|
||||
kfree(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t->tx_cxt = tx;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void free_tx_struct(struct sdio_tx *t)
|
||||
{
|
||||
if (t) {
|
||||
kfree(t->buf);
|
||||
kfree(t);
|
||||
}
|
||||
}
|
||||
|
||||
static struct sdio_rx *alloc_rx_struct(struct rx_cxt *rx)
|
||||
{
|
||||
struct sdio_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC);
|
||||
|
||||
if (r)
|
||||
r->rx_cxt = rx;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static void free_rx_struct(struct sdio_rx *r)
|
||||
{
|
||||
kfree(r);
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static struct sdio_tx *get_tx_struct(struct tx_cxt *tx, int *no_spc)
|
||||
{
|
||||
struct sdio_tx *t;
|
||||
|
||||
if (list_empty(&tx->free_list))
|
||||
return NULL;
|
||||
|
||||
t = list_entry(tx->free_list.prev, struct sdio_tx, list);
|
||||
list_del(&t->list);
|
||||
|
||||
*no_spc = list_empty(&tx->free_list) ? 1 : 0;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static void put_tx_struct(struct tx_cxt *tx, struct sdio_tx *t)
|
||||
{
|
||||
list_add_tail(&t->list, &tx->free_list);
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static struct sdio_rx *get_rx_struct(struct rx_cxt *rx)
|
||||
{
|
||||
struct sdio_rx *r;
|
||||
|
||||
if (list_empty(&rx->free_list))
|
||||
return NULL;
|
||||
|
||||
r = list_entry(rx->free_list.prev, struct sdio_rx, list);
|
||||
list_del(&r->list);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static void put_rx_struct(struct rx_cxt *rx, struct sdio_rx *r)
|
||||
{
|
||||
list_add_tail(&r->list, &rx->free_list);
|
||||
}
|
||||
|
||||
static void release_sdio(struct sdiowm_dev *sdev)
|
||||
{
|
||||
struct tx_cxt *tx = &sdev->tx;
|
||||
struct rx_cxt *rx = &sdev->rx;
|
||||
struct sdio_tx *t, *t_next;
|
||||
struct sdio_rx *r, *r_next;
|
||||
|
||||
kfree(tx->sdu_buf);
|
||||
|
||||
list_for_each_entry_safe(t, t_next, &tx->free_list, list) {
|
||||
list_del(&t->list);
|
||||
free_tx_struct(t);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) {
|
||||
list_del(&t->list);
|
||||
free_tx_struct(t);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(t, t_next, &tx->hci_list, list) {
|
||||
list_del(&t->list);
|
||||
free_tx_struct(t);
|
||||
}
|
||||
|
||||
kfree(rx->rx_buf);
|
||||
|
||||
list_for_each_entry_safe(r, r_next, &rx->free_list, list) {
|
||||
list_del(&r->list);
|
||||
free_rx_struct(r);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(r, r_next, &rx->req_list, list) {
|
||||
list_del(&r->list);
|
||||
free_rx_struct(r);
|
||||
}
|
||||
}
|
||||
|
||||
static int init_sdio(struct sdiowm_dev *sdev)
|
||||
{
|
||||
int ret = 0, i;
|
||||
struct tx_cxt *tx = &sdev->tx;
|
||||
struct rx_cxt *rx = &sdev->rx;
|
||||
struct sdio_tx *t;
|
||||
struct sdio_rx *r;
|
||||
|
||||
INIT_LIST_HEAD(&tx->free_list);
|
||||
INIT_LIST_HEAD(&tx->sdu_list);
|
||||
INIT_LIST_HEAD(&tx->hci_list);
|
||||
|
||||
spin_lock_init(&tx->lock);
|
||||
|
||||
tx->sdu_buf = kmalloc(SDU_TX_BUF_SIZE, GFP_KERNEL);
|
||||
if (!tx->sdu_buf)
|
||||
goto fail;
|
||||
|
||||
for (i = 0; i < MAX_NR_SDU_BUF; i++) {
|
||||
t = alloc_tx_struct(tx);
|
||||
if (!t) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
list_add(&t->list, &tx->free_list);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&rx->free_list);
|
||||
INIT_LIST_HEAD(&rx->req_list);
|
||||
|
||||
spin_lock_init(&rx->lock);
|
||||
|
||||
for (i = 0; i < MAX_NR_RX_BUF; i++) {
|
||||
r = alloc_rx_struct(rx);
|
||||
if (!r) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
list_add(&r->list, &rx->free_list);
|
||||
}
|
||||
|
||||
rx->rx_buf = kmalloc(RX_BUF_SIZE, GFP_KERNEL);
|
||||
if (!rx->rx_buf)
|
||||
goto fail;
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
release_sdio(sdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void send_sdio_pkt(struct sdio_func *func, u8 *data, int len)
|
||||
{
|
||||
int n, blocks, ret, remain;
|
||||
|
||||
sdio_claim_host(func);
|
||||
|
||||
blocks = len / func->cur_blksize;
|
||||
n = blocks * func->cur_blksize;
|
||||
if (blocks) {
|
||||
ret = sdio_memcpy_toio(func, 0, data, n);
|
||||
if (ret < 0) {
|
||||
if (ret != -ENOMEDIUM)
|
||||
dev_err(&func->dev,
|
||||
"gdmwms: error: ret = %d\n", ret);
|
||||
goto end_io;
|
||||
}
|
||||
}
|
||||
|
||||
remain = len - n;
|
||||
remain = (remain + 3) & ~3;
|
||||
|
||||
if (remain) {
|
||||
ret = sdio_memcpy_toio(func, 0, data + n, remain);
|
||||
if (ret < 0) {
|
||||
if (ret != -ENOMEDIUM)
|
||||
dev_err(&func->dev,
|
||||
"gdmwms: error: ret = %d\n", ret);
|
||||
goto end_io;
|
||||
}
|
||||
}
|
||||
|
||||
end_io:
|
||||
sdio_release_host(func);
|
||||
}
|
||||
|
||||
static void send_sdu(struct sdio_func *func, struct tx_cxt *tx)
|
||||
{
|
||||
struct list_head *l, *next;
|
||||
struct hci_s *hci;
|
||||
struct sdio_tx *t;
|
||||
int pos, len, i, estlen, aggr_num = 0, aggr_len;
|
||||
u8 *buf;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
pos = TYPE_A_HEADER_SIZE + HCI_HEADER_SIZE;
|
||||
list_for_each_entry(t, &tx->sdu_list, list) {
|
||||
estlen = ((t->len + 3) & ~3) + 4;
|
||||
if ((pos + estlen) > SDU_TX_BUF_SIZE)
|
||||
break;
|
||||
|
||||
aggr_num++;
|
||||
memcpy(tx->sdu_buf + pos, t->buf, t->len);
|
||||
memset(tx->sdu_buf + pos + t->len, 0, estlen - t->len);
|
||||
pos += estlen;
|
||||
}
|
||||
aggr_len = pos;
|
||||
|
||||
hci = (struct hci_s *)(tx->sdu_buf + TYPE_A_HEADER_SIZE);
|
||||
hci->cmd_evt = cpu_to_be16(WIMAX_TX_SDU_AGGR);
|
||||
hci->length = cpu_to_be16(aggr_len - TYPE_A_HEADER_SIZE -
|
||||
HCI_HEADER_SIZE);
|
||||
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
dev_dbg(&func->dev, "sdio_send: %*ph\n", aggr_len - TYPE_A_HEADER_SIZE,
|
||||
tx->sdu_buf + TYPE_A_HEADER_SIZE);
|
||||
|
||||
for (pos = TYPE_A_HEADER_SIZE; pos < aggr_len; pos += TX_CHUNK_SIZE) {
|
||||
len = aggr_len - pos;
|
||||
len = len > TX_CHUNK_SIZE ? TX_CHUNK_SIZE : len;
|
||||
buf = tx->sdu_buf + pos - TYPE_A_HEADER_SIZE;
|
||||
|
||||
buf[0] = len & 0xff;
|
||||
buf[1] = (len >> 8) & 0xff;
|
||||
buf[2] = (len >> 16) & 0xff;
|
||||
buf[3] = (pos + len) >= aggr_len ? 0 : 1;
|
||||
send_sdio_pkt(func, buf, len + TYPE_A_HEADER_SIZE);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
for (l = tx->sdu_list.next, i = 0; i < aggr_num; i++, l = next) {
|
||||
next = l->next;
|
||||
t = list_entry(l, struct sdio_tx, list);
|
||||
if (t->callback)
|
||||
t->callback(t->cb_data);
|
||||
|
||||
list_del(l);
|
||||
put_tx_struct(t->tx_cxt, t);
|
||||
}
|
||||
|
||||
tx->sdu_stamp = ktime_get();
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
}
|
||||
|
||||
static void send_hci(struct sdio_func *func, struct tx_cxt *tx,
|
||||
struct sdio_tx *t)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
dev_dbg(&func->dev, "sdio_send: %*ph\n", t->len - TYPE_A_HEADER_SIZE,
|
||||
t->buf + TYPE_A_HEADER_SIZE);
|
||||
|
||||
send_sdio_pkt(func, t->buf, t->len);
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
if (t->callback)
|
||||
t->callback(t->cb_data);
|
||||
free_tx_struct(t);
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
}
|
||||
|
||||
static void do_tx(struct work_struct *work)
|
||||
{
|
||||
struct sdiowm_dev *sdev = container_of(work, struct sdiowm_dev, ws);
|
||||
struct sdio_func *func = sdev->func;
|
||||
struct tx_cxt *tx = &sdev->tx;
|
||||
struct sdio_tx *t = NULL;
|
||||
ktime_t now, before;
|
||||
int is_sdu = 0;
|
||||
long diff;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
if (!tx->can_send) {
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!list_empty(&tx->hci_list)) {
|
||||
t = list_entry(tx->hci_list.next, struct sdio_tx, list);
|
||||
list_del(&t->list);
|
||||
is_sdu = 0;
|
||||
} else if (!tx->stop_sdu_tx && !list_empty(&tx->sdu_list)) {
|
||||
now = ktime_get();
|
||||
before = tx->sdu_stamp;
|
||||
|
||||
diff = ktime_to_ns(ktime_sub(now, before));
|
||||
if (diff >= 0 && diff < TX_INTERVAL) {
|
||||
schedule_work(&sdev->ws);
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return;
|
||||
}
|
||||
is_sdu = 1;
|
||||
}
|
||||
|
||||
if (!is_sdu && !t) {
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return;
|
||||
}
|
||||
|
||||
tx->can_send = 0;
|
||||
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
if (is_sdu)
|
||||
send_sdu(func, tx);
|
||||
else
|
||||
send_hci(func, tx, t);
|
||||
}
|
||||
|
||||
static int gdm_sdio_send(void *priv_dev, void *data, int len,
|
||||
void (*cb)(void *data), void *cb_data)
|
||||
{
|
||||
struct sdiowm_dev *sdev = priv_dev;
|
||||
struct tx_cxt *tx = &sdev->tx;
|
||||
struct sdio_tx *t;
|
||||
u8 *pkt = data;
|
||||
int no_spc = 0;
|
||||
u16 cmd_evt;
|
||||
unsigned long flags;
|
||||
|
||||
if (len > TX_BUF_SIZE - TYPE_A_HEADER_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
cmd_evt = (pkt[0] << 8) | pkt[1];
|
||||
if (cmd_evt == WIMAX_TX_SDU) {
|
||||
t = get_tx_struct(tx, &no_spc);
|
||||
if (!t) {
|
||||
/* This case must not happen. */
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return -ENOSPC;
|
||||
}
|
||||
list_add_tail(&t->list, &tx->sdu_list);
|
||||
|
||||
memcpy(t->buf, data, len);
|
||||
|
||||
t->len = len;
|
||||
t->callback = cb;
|
||||
t->cb_data = cb_data;
|
||||
} else {
|
||||
t = alloc_tx_struct(tx);
|
||||
if (!t) {
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
list_add_tail(&t->list, &tx->hci_list);
|
||||
|
||||
t->buf[0] = len & 0xff;
|
||||
t->buf[1] = (len >> 8) & 0xff;
|
||||
t->buf[2] = (len >> 16) & 0xff;
|
||||
t->buf[3] = 2;
|
||||
memcpy(t->buf + TYPE_A_HEADER_SIZE, data, len);
|
||||
|
||||
t->len = len + TYPE_A_HEADER_SIZE;
|
||||
t->callback = cb;
|
||||
t->cb_data = cb_data;
|
||||
}
|
||||
|
||||
if (tx->can_send)
|
||||
schedule_work(&sdev->ws);
|
||||
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
if (no_spc)
|
||||
return -ENOSPC;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Handle the HCI, WIMAX_SDU_TX_FLOW. */
|
||||
static int control_sdu_tx_flow(struct sdiowm_dev *sdev, u8 *hci_data, int len)
|
||||
{
|
||||
struct tx_cxt *tx = &sdev->tx;
|
||||
u16 cmd_evt;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
cmd_evt = (hci_data[0] << 8) | (hci_data[1]);
|
||||
if (cmd_evt != WIMAX_SDU_TX_FLOW)
|
||||
goto out;
|
||||
|
||||
if (hci_data[4] == 0) {
|
||||
dev_dbg(&sdev->func->dev, "WIMAX ==> STOP SDU TX\n");
|
||||
tx->stop_sdu_tx = 1;
|
||||
} else if (hci_data[4] == 1) {
|
||||
dev_dbg(&sdev->func->dev, "WIMAX ==> START SDU TX\n");
|
||||
tx->stop_sdu_tx = 0;
|
||||
if (tx->can_send)
|
||||
schedule_work(&sdev->ws);
|
||||
/* If free buffer for sdu tx doesn't exist, then tx queue
|
||||
* should not be woken. For this reason, don't pass the command,
|
||||
* START_SDU_TX.
|
||||
*/
|
||||
if (list_empty(&tx->free_list))
|
||||
len = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return len;
|
||||
}
|
||||
|
||||
static void gdm_sdio_irq(struct sdio_func *func)
|
||||
{
|
||||
struct phy_dev *phy_dev = sdio_get_drvdata(func);
|
||||
struct sdiowm_dev *sdev = phy_dev->priv_dev;
|
||||
struct tx_cxt *tx = &sdev->tx;
|
||||
struct rx_cxt *rx = &sdev->rx;
|
||||
struct sdio_rx *r;
|
||||
unsigned long flags;
|
||||
u8 val, hdr[TYPE_A_LOOKAHEAD_SIZE], *buf;
|
||||
u32 len, blocks, n;
|
||||
int ret, remain;
|
||||
|
||||
/* Check interrupt */
|
||||
val = sdio_readb(func, 0x13, &ret);
|
||||
if (val & 0x01)
|
||||
sdio_writeb(func, 0x01, 0x13, &ret); /* clear interrupt */
|
||||
else
|
||||
return;
|
||||
|
||||
ret = sdio_memcpy_fromio(func, hdr, 0x0, TYPE_A_LOOKAHEAD_SIZE);
|
||||
if (ret) {
|
||||
dev_err(&func->dev,
|
||||
"Cannot read from function %d\n", func->num);
|
||||
goto done;
|
||||
}
|
||||
|
||||
len = (hdr[2] << 16) | (hdr[1] << 8) | hdr[0];
|
||||
if (len > (RX_BUF_SIZE - TYPE_A_HEADER_SIZE)) {
|
||||
dev_err(&func->dev, "Too big Type-A size: %d\n", len);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (hdr[3] == 1) { /* Ack */
|
||||
u32 *ack_seq = (u32 *)&hdr[4];
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
tx->can_send = 1;
|
||||
|
||||
if (!list_empty(&tx->sdu_list) || !list_empty(&tx->hci_list))
|
||||
schedule_work(&sdev->ws);
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
dev_dbg(&func->dev, "Ack... %0x\n", ntohl(*ack_seq));
|
||||
goto done;
|
||||
}
|
||||
|
||||
memcpy(rx->rx_buf, hdr + TYPE_A_HEADER_SIZE,
|
||||
TYPE_A_LOOKAHEAD_SIZE - TYPE_A_HEADER_SIZE);
|
||||
|
||||
buf = rx->rx_buf + TYPE_A_LOOKAHEAD_SIZE - TYPE_A_HEADER_SIZE;
|
||||
remain = len - TYPE_A_LOOKAHEAD_SIZE + TYPE_A_HEADER_SIZE;
|
||||
if (remain <= 0)
|
||||
goto end_io;
|
||||
|
||||
blocks = remain / func->cur_blksize;
|
||||
|
||||
if (blocks) {
|
||||
n = blocks * func->cur_blksize;
|
||||
ret = sdio_memcpy_fromio(func, buf, 0x0, n);
|
||||
if (ret) {
|
||||
dev_err(&func->dev,
|
||||
"Cannot read from function %d\n", func->num);
|
||||
goto done;
|
||||
}
|
||||
buf += n;
|
||||
remain -= n;
|
||||
}
|
||||
|
||||
if (remain) {
|
||||
ret = sdio_memcpy_fromio(func, buf, 0x0, remain);
|
||||
if (ret) {
|
||||
dev_err(&func->dev,
|
||||
"Cannot read from function %d\n", func->num);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
end_io:
|
||||
dev_dbg(&func->dev, "sdio_receive: %*ph\n", len, rx->rx_buf);
|
||||
|
||||
len = control_sdu_tx_flow(sdev, rx->rx_buf, len);
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
|
||||
if (!list_empty(&rx->req_list)) {
|
||||
r = list_entry(rx->req_list.next, struct sdio_rx, list);
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
if (r->callback)
|
||||
r->callback(r->cb_data, rx->rx_buf, len);
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
list_del(&r->list);
|
||||
put_rx_struct(rx, r);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
|
||||
done:
|
||||
sdio_writeb(func, 0x00, 0x10, &ret); /* PCRRT */
|
||||
if (!phy_dev->netdev)
|
||||
register_wimax_device(phy_dev, &func->dev);
|
||||
}
|
||||
|
||||
static int gdm_sdio_receive(void *priv_dev,
|
||||
void (*cb)(void *cb_data, void *data, int len),
|
||||
void *cb_data)
|
||||
{
|
||||
struct sdiowm_dev *sdev = priv_dev;
|
||||
struct rx_cxt *rx = &sdev->rx;
|
||||
struct sdio_rx *r;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
r = get_rx_struct(rx);
|
||||
if (!r) {
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r->callback = cb;
|
||||
r->cb_data = cb_data;
|
||||
|
||||
list_add_tail(&r->list, &rx->req_list);
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sdio_wimax_probe(struct sdio_func *func,
|
||||
const struct sdio_device_id *id)
|
||||
{
|
||||
int ret;
|
||||
struct phy_dev *phy_dev = NULL;
|
||||
struct sdiowm_dev *sdev = NULL;
|
||||
|
||||
dev_info(&func->dev, "Found GDM SDIO VID = 0x%04x PID = 0x%04x...\n",
|
||||
func->vendor, func->device);
|
||||
dev_info(&func->dev, "GCT WiMax driver version %s\n", DRIVER_VERSION);
|
||||
|
||||
sdio_claim_host(func);
|
||||
sdio_enable_func(func);
|
||||
sdio_claim_irq(func, gdm_sdio_irq);
|
||||
|
||||
ret = sdio_boot(func);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL);
|
||||
if (!phy_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
sdev = kzalloc(sizeof(*sdev), GFP_KERNEL);
|
||||
if (!sdev) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
phy_dev->priv_dev = (void *)sdev;
|
||||
phy_dev->send_func = gdm_sdio_send;
|
||||
phy_dev->rcv_func = gdm_sdio_receive;
|
||||
|
||||
ret = init_sdio(sdev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
sdev->func = func;
|
||||
|
||||
sdio_writeb(func, 1, 0x14, &ret); /* Enable interrupt */
|
||||
sdio_release_host(func);
|
||||
|
||||
INIT_WORK(&sdev->ws, do_tx);
|
||||
|
||||
sdio_set_drvdata(func, phy_dev);
|
||||
out:
|
||||
if (ret) {
|
||||
kfree(phy_dev);
|
||||
kfree(sdev);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void sdio_wimax_remove(struct sdio_func *func)
|
||||
{
|
||||
struct phy_dev *phy_dev = sdio_get_drvdata(func);
|
||||
struct sdiowm_dev *sdev = phy_dev->priv_dev;
|
||||
|
||||
cancel_work_sync(&sdev->ws);
|
||||
if (phy_dev->netdev)
|
||||
unregister_wimax_device(phy_dev);
|
||||
sdio_claim_host(func);
|
||||
sdio_release_irq(func);
|
||||
sdio_disable_func(func);
|
||||
sdio_release_host(func);
|
||||
release_sdio(sdev);
|
||||
|
||||
kfree(sdev);
|
||||
kfree(phy_dev);
|
||||
}
|
||||
|
||||
static const struct sdio_device_id sdio_wimax_ids[] = {
|
||||
{ SDIO_DEVICE(0x0296, 0x5347) },
|
||||
{0}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(sdio, sdio_wimax_ids);
|
||||
|
||||
static struct sdio_driver sdio_wimax_driver = {
|
||||
.probe = sdio_wimax_probe,
|
||||
.remove = sdio_wimax_remove,
|
||||
.name = "sdio_wimax",
|
||||
.id_table = sdio_wimax_ids,
|
||||
};
|
||||
|
||||
static int __init sdio_gdm_wimax_init(void)
|
||||
{
|
||||
return sdio_register_driver(&sdio_wimax_driver);
|
||||
}
|
||||
|
||||
static void __exit sdio_gdm_wimax_exit(void)
|
||||
{
|
||||
sdio_unregister_driver(&sdio_wimax_driver);
|
||||
}
|
||||
|
||||
module_init(sdio_gdm_wimax_init);
|
||||
module_exit(sdio_gdm_wimax_exit);
|
||||
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_DESCRIPTION("GCT WiMax SDIO Device Driver");
|
||||
MODULE_AUTHOR("Ethan Park");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,63 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_GDM_SDIO_H__
|
||||
#define __GDM72XX_GDM_SDIO_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ktime.h>
|
||||
|
||||
#define MAX_NR_SDU_BUF 64
|
||||
|
||||
struct sdio_tx {
|
||||
struct list_head list;
|
||||
struct tx_cxt *tx_cxt;
|
||||
u8 *buf;
|
||||
int len;
|
||||
void (*callback)(void *cb_data);
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct tx_cxt {
|
||||
struct list_head free_list;
|
||||
struct list_head sdu_list;
|
||||
struct list_head hci_list;
|
||||
ktime_t sdu_stamp;
|
||||
u8 *sdu_buf;
|
||||
spinlock_t lock; /* protect structure fields */
|
||||
int can_send;
|
||||
int stop_sdu_tx;
|
||||
};
|
||||
|
||||
struct sdio_rx {
|
||||
struct list_head list;
|
||||
struct rx_cxt *rx_cxt;
|
||||
void (*callback)(void *cb_data, void *data, int len);
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct rx_cxt {
|
||||
struct list_head free_list;
|
||||
struct list_head req_list;
|
||||
u8 *rx_buf;
|
||||
spinlock_t lock; /* protect structure fields */
|
||||
};
|
||||
|
||||
struct sdiowm_dev {
|
||||
struct sdio_func *func;
|
||||
struct tx_cxt tx;
|
||||
struct rx_cxt rx;
|
||||
struct work_struct ws;
|
||||
};
|
||||
|
||||
#endif /* __GDM72XX_GDM_SDIO_H__ */
|
|
@ -1,788 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/usb.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/kthread.h>
|
||||
|
||||
#include "gdm_usb.h"
|
||||
#include "gdm_wimax.h"
|
||||
#include "usb_boot.h"
|
||||
#include "hci.h"
|
||||
|
||||
#include "usb_ids.h"
|
||||
|
||||
MODULE_DEVICE_TABLE(usb, id_table);
|
||||
|
||||
#define TX_BUF_SIZE 2048
|
||||
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_WIMAX2)
|
||||
#define RX_BUF_SIZE (128 * 1024) /* For packet aggregation */
|
||||
#else
|
||||
#define RX_BUF_SIZE 2048
|
||||
#endif
|
||||
|
||||
#define GDM7205_PADDING 256
|
||||
|
||||
#define DOWNLOAD_CONF_VALUE 0x21
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
|
||||
|
||||
static DECLARE_WAIT_QUEUE_HEAD(k_wait);
|
||||
static LIST_HEAD(k_list);
|
||||
static DEFINE_SPINLOCK(k_lock);
|
||||
static int k_mode_stop;
|
||||
|
||||
#define K_WAIT_TIME (2 * HZ / 100)
|
||||
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
|
||||
|
||||
static struct usb_tx *alloc_tx_struct(struct tx_cxt *tx)
|
||||
{
|
||||
struct usb_tx *t = kzalloc(sizeof(*t), GFP_ATOMIC);
|
||||
|
||||
if (!t)
|
||||
return NULL;
|
||||
|
||||
t->urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
t->buf = kmalloc(TX_BUF_SIZE, GFP_ATOMIC);
|
||||
if (!t->urb || !t->buf) {
|
||||
usb_free_urb(t->urb);
|
||||
kfree(t->buf);
|
||||
kfree(t);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t->tx_cxt = tx;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
static void free_tx_struct(struct usb_tx *t)
|
||||
{
|
||||
if (t) {
|
||||
usb_free_urb(t->urb);
|
||||
kfree(t->buf);
|
||||
kfree(t);
|
||||
}
|
||||
}
|
||||
|
||||
static struct usb_rx *alloc_rx_struct(struct rx_cxt *rx)
|
||||
{
|
||||
struct usb_rx *r = kzalloc(sizeof(*r), GFP_ATOMIC);
|
||||
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
r->urb = usb_alloc_urb(0, GFP_ATOMIC);
|
||||
r->buf = kmalloc(RX_BUF_SIZE, GFP_ATOMIC);
|
||||
if (!r->urb || !r->buf) {
|
||||
usb_free_urb(r->urb);
|
||||
kfree(r->buf);
|
||||
kfree(r);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
r->rx_cxt = rx;
|
||||
return r;
|
||||
}
|
||||
|
||||
static void free_rx_struct(struct usb_rx *r)
|
||||
{
|
||||
if (r) {
|
||||
usb_free_urb(r->urb);
|
||||
kfree(r->buf);
|
||||
kfree(r);
|
||||
}
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static struct usb_tx *get_tx_struct(struct tx_cxt *tx, int *no_spc)
|
||||
{
|
||||
struct usb_tx *t;
|
||||
|
||||
if (list_empty(&tx->free_list)) {
|
||||
*no_spc = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
t = list_entry(tx->free_list.next, struct usb_tx, list);
|
||||
list_del(&t->list);
|
||||
|
||||
*no_spc = list_empty(&tx->free_list) ? 1 : 0;
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static void put_tx_struct(struct tx_cxt *tx, struct usb_tx *t)
|
||||
{
|
||||
list_add_tail(&t->list, &tx->free_list);
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static struct usb_rx *get_rx_struct(struct rx_cxt *rx)
|
||||
{
|
||||
struct usb_rx *r;
|
||||
|
||||
if (list_empty(&rx->free_list)) {
|
||||
r = alloc_rx_struct(rx);
|
||||
if (!r)
|
||||
return NULL;
|
||||
|
||||
list_add(&r->list, &rx->free_list);
|
||||
}
|
||||
|
||||
r = list_entry(rx->free_list.next, struct usb_rx, list);
|
||||
list_move_tail(&r->list, &rx->used_list);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Before this function is called, spin lock should be locked. */
|
||||
static void put_rx_struct(struct rx_cxt *rx, struct usb_rx *r)
|
||||
{
|
||||
list_move(&r->list, &rx->free_list);
|
||||
}
|
||||
|
||||
static void release_usb(struct usbwm_dev *udev)
|
||||
{
|
||||
struct tx_cxt *tx = &udev->tx;
|
||||
struct rx_cxt *rx = &udev->rx;
|
||||
struct usb_tx *t, *t_next;
|
||||
struct usb_rx *r, *r_next;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(t, t_next, &tx->sdu_list, list) {
|
||||
list_del(&t->list);
|
||||
free_tx_struct(t);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(t, t_next, &tx->hci_list, list) {
|
||||
list_del(&t->list);
|
||||
free_tx_struct(t);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(t, t_next, &tx->free_list, list) {
|
||||
list_del(&t->list);
|
||||
free_tx_struct(t);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(r, r_next, &rx->free_list, list) {
|
||||
list_del(&r->list);
|
||||
free_rx_struct(r);
|
||||
}
|
||||
|
||||
list_for_each_entry_safe(r, r_next, &rx->used_list, list) {
|
||||
list_del(&r->list);
|
||||
free_rx_struct(r);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
}
|
||||
|
||||
static int init_usb(struct usbwm_dev *udev)
|
||||
{
|
||||
int ret = 0, i;
|
||||
struct tx_cxt *tx = &udev->tx;
|
||||
struct rx_cxt *rx = &udev->rx;
|
||||
struct usb_tx *t;
|
||||
struct usb_rx *r;
|
||||
unsigned long flags;
|
||||
|
||||
INIT_LIST_HEAD(&tx->free_list);
|
||||
INIT_LIST_HEAD(&tx->sdu_list);
|
||||
INIT_LIST_HEAD(&tx->hci_list);
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
|
||||
INIT_LIST_HEAD(&tx->pending_list);
|
||||
#endif
|
||||
|
||||
INIT_LIST_HEAD(&rx->free_list);
|
||||
INIT_LIST_HEAD(&rx->used_list);
|
||||
|
||||
spin_lock_init(&tx->lock);
|
||||
spin_lock_init(&rx->lock);
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
for (i = 0; i < MAX_NR_SDU_BUF; i++) {
|
||||
t = alloc_tx_struct(tx);
|
||||
if (!t) {
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
list_add(&t->list, &tx->free_list);
|
||||
}
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
r = alloc_rx_struct(rx);
|
||||
if (!r) {
|
||||
ret = -ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
list_add(&r->list, &rx->free_list);
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
return ret;
|
||||
|
||||
fail:
|
||||
release_usb(udev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __gdm_usb_send_complete(struct urb *urb)
|
||||
{
|
||||
struct usb_tx *t = urb->context;
|
||||
struct tx_cxt *tx = t->tx_cxt;
|
||||
u8 *pkt = t->buf;
|
||||
u16 cmd_evt;
|
||||
|
||||
/* Completion by usb_unlink_urb */
|
||||
if (urb->status == -ECONNRESET)
|
||||
return;
|
||||
|
||||
if (t->callback)
|
||||
t->callback(t->cb_data);
|
||||
|
||||
/* Delete from sdu list or hci list. */
|
||||
list_del(&t->list);
|
||||
|
||||
cmd_evt = (pkt[0] << 8) | pkt[1];
|
||||
if (cmd_evt == WIMAX_TX_SDU)
|
||||
put_tx_struct(tx, t);
|
||||
else
|
||||
free_tx_struct(t);
|
||||
}
|
||||
|
||||
static void gdm_usb_send_complete(struct urb *urb)
|
||||
{
|
||||
struct usb_tx *t = urb->context;
|
||||
struct tx_cxt *tx = t->tx_cxt;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
__gdm_usb_send_complete(urb);
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
}
|
||||
|
||||
static int gdm_usb_send(void *priv_dev, void *data, size_t len,
|
||||
void (*cb)(void *data), void *cb_data)
|
||||
{
|
||||
struct usbwm_dev *udev = priv_dev;
|
||||
struct usb_device *usbdev = udev->usbdev;
|
||||
struct tx_cxt *tx = &udev->tx;
|
||||
struct usb_tx *t;
|
||||
int padding = udev->padding;
|
||||
int no_spc = 0, ret;
|
||||
u8 *pkt = data;
|
||||
u16 cmd_evt;
|
||||
unsigned long flags;
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
|
||||
unsigned long flags2;
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
|
||||
|
||||
if (!udev->usbdev) {
|
||||
dev_err(&usbdev->dev, "%s: No such device\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (len > TX_BUF_SIZE - padding - 1)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
cmd_evt = (pkt[0] << 8) | pkt[1];
|
||||
if (cmd_evt == WIMAX_TX_SDU) {
|
||||
t = get_tx_struct(tx, &no_spc);
|
||||
if (!t) {
|
||||
/* This case must not happen. */
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return -ENOSPC;
|
||||
}
|
||||
list_add_tail(&t->list, &tx->sdu_list);
|
||||
} else {
|
||||
t = alloc_tx_struct(tx);
|
||||
if (!t) {
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
list_add_tail(&t->list, &tx->hci_list);
|
||||
}
|
||||
|
||||
memcpy(t->buf + padding, data, len);
|
||||
t->callback = cb;
|
||||
t->cb_data = cb_data;
|
||||
|
||||
/* In some cases, USB Module of WiMax is blocked when data size is
|
||||
* the multiple of 512. So, increment length by one in that case.
|
||||
*/
|
||||
if ((len % 512) == 0)
|
||||
len++;
|
||||
|
||||
usb_fill_bulk_urb(t->urb, usbdev, usb_sndbulkpipe(usbdev, 1), t->buf,
|
||||
len + padding, gdm_usb_send_complete, t);
|
||||
|
||||
dev_dbg(&usbdev->dev, "usb_send: %*ph\n", (int)len + padding, t->buf);
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
if (usbdev->state & USB_STATE_SUSPENDED) {
|
||||
list_add_tail(&t->p_list, &tx->pending_list);
|
||||
schedule_work(&udev->pm_ws);
|
||||
goto out;
|
||||
}
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
|
||||
if (udev->bw_switch) {
|
||||
list_add_tail(&t->p_list, &tx->pending_list);
|
||||
goto out;
|
||||
} else if (cmd_evt == WIMAX_SCAN) {
|
||||
struct rx_cxt *rx;
|
||||
struct usb_rx *r;
|
||||
|
||||
rx = &udev->rx;
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags2);
|
||||
list_for_each_entry(r, &rx->used_list, list)
|
||||
usb_unlink_urb(r->urb);
|
||||
spin_unlock_irqrestore(&rx->lock, flags2);
|
||||
|
||||
udev->bw_switch = 1;
|
||||
|
||||
spin_lock_irqsave(&k_lock, flags2);
|
||||
list_add_tail(&udev->list, &k_list);
|
||||
spin_unlock_irqrestore(&k_lock, flags2);
|
||||
|
||||
wake_up(&k_wait);
|
||||
}
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
|
||||
|
||||
ret = usb_submit_urb(t->urb, GFP_ATOMIC);
|
||||
if (ret)
|
||||
goto send_fail;
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
usb_mark_last_busy(usbdev);
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
|
||||
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
|
||||
out:
|
||||
#endif
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
if (no_spc)
|
||||
return -ENOSPC;
|
||||
|
||||
return 0;
|
||||
|
||||
send_fail:
|
||||
t->callback = NULL;
|
||||
__gdm_usb_send_complete(t->urb);
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gdm_usb_rcv_complete(struct urb *urb)
|
||||
{
|
||||
struct usb_rx *r = urb->context;
|
||||
struct rx_cxt *rx = r->rx_cxt;
|
||||
struct usbwm_dev *udev = container_of(r->rx_cxt, struct usbwm_dev, rx);
|
||||
struct tx_cxt *tx = &udev->tx;
|
||||
struct usb_tx *t;
|
||||
u16 cmd_evt;
|
||||
unsigned long flags, flags2;
|
||||
struct usb_device *dev = urb->dev;
|
||||
|
||||
/* Completion by usb_unlink_urb */
|
||||
if (urb->status == -ECONNRESET)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
if (!urb->status) {
|
||||
cmd_evt = (r->buf[0] << 8) | (r->buf[1]);
|
||||
|
||||
dev_dbg(&dev->dev, "usb_receive: %*ph\n", urb->actual_length,
|
||||
r->buf);
|
||||
|
||||
if (cmd_evt == WIMAX_SDU_TX_FLOW) {
|
||||
if (r->buf[4] == 0) {
|
||||
dev_dbg(&dev->dev, "WIMAX ==> STOP SDU TX\n");
|
||||
list_for_each_entry(t, &tx->sdu_list, list)
|
||||
usb_unlink_urb(t->urb);
|
||||
} else if (r->buf[4] == 1) {
|
||||
dev_dbg(&dev->dev, "WIMAX ==> START SDU TX\n");
|
||||
list_for_each_entry(t, &tx->sdu_list, list) {
|
||||
usb_submit_urb(t->urb, GFP_ATOMIC);
|
||||
}
|
||||
/* If free buffer for sdu tx doesn't
|
||||
* exist, then tx queue should not be
|
||||
* woken. For this reason, don't pass
|
||||
* the command, START_SDU_TX.
|
||||
*/
|
||||
if (list_empty(&tx->free_list))
|
||||
urb->actual_length = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!urb->status && r->callback)
|
||||
r->callback(r->cb_data, r->buf, urb->actual_length);
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags2);
|
||||
put_rx_struct(rx, r);
|
||||
spin_unlock_irqrestore(&rx->lock, flags2);
|
||||
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
usb_mark_last_busy(dev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int gdm_usb_receive(void *priv_dev,
|
||||
void (*cb)(void *cb_data, void *data, size_t len),
|
||||
void *cb_data)
|
||||
{
|
||||
struct usbwm_dev *udev = priv_dev;
|
||||
struct usb_device *usbdev = udev->usbdev;
|
||||
struct rx_cxt *rx = &udev->rx;
|
||||
struct usb_rx *r;
|
||||
unsigned long flags;
|
||||
|
||||
if (!udev->usbdev) {
|
||||
dev_err(&usbdev->dev, "%s: No such device\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
r = get_rx_struct(rx);
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
|
||||
if (!r)
|
||||
return -ENOMEM;
|
||||
|
||||
r->callback = cb;
|
||||
r->cb_data = cb_data;
|
||||
|
||||
usb_fill_bulk_urb(r->urb, usbdev, usb_rcvbulkpipe(usbdev, 0x82), r->buf,
|
||||
RX_BUF_SIZE, gdm_usb_rcv_complete, r);
|
||||
|
||||
return usb_submit_urb(r->urb, GFP_ATOMIC);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
static void do_pm_control(struct work_struct *work)
|
||||
{
|
||||
struct usbwm_dev *udev = container_of(work, struct usbwm_dev, pm_ws);
|
||||
struct tx_cxt *tx = &udev->tx;
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
|
||||
ret = usb_autopm_get_interface(udev->intf);
|
||||
if (!ret)
|
||||
usb_autopm_put_interface(udev->intf);
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
if (!(udev->usbdev->state & USB_STATE_SUSPENDED) &&
|
||||
(!list_empty(&tx->hci_list) || !list_empty(&tx->sdu_list))) {
|
||||
struct usb_tx *t, *temp;
|
||||
|
||||
list_for_each_entry_safe(t, temp, &tx->pending_list, p_list) {
|
||||
list_del(&t->p_list);
|
||||
ret = usb_submit_urb(t->urb, GFP_ATOMIC);
|
||||
|
||||
if (ret) {
|
||||
t->callback = NULL;
|
||||
__gdm_usb_send_complete(t->urb);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
}
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
|
||||
|
||||
static int gdm_usb_probe(struct usb_interface *intf,
|
||||
const struct usb_device_id *id)
|
||||
{
|
||||
int ret = 0;
|
||||
u8 bConfigurationValue;
|
||||
struct phy_dev *phy_dev = NULL;
|
||||
struct usbwm_dev *udev = NULL;
|
||||
u16 idVendor, idProduct, bcdDevice;
|
||||
|
||||
struct usb_device *usbdev = interface_to_usbdev(intf);
|
||||
|
||||
usb_get_dev(usbdev);
|
||||
bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue;
|
||||
|
||||
/*USB description is set up with Little-Endian*/
|
||||
idVendor = le16_to_cpu(usbdev->descriptor.idVendor);
|
||||
idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
|
||||
bcdDevice = le16_to_cpu(usbdev->descriptor.bcdDevice);
|
||||
|
||||
dev_info(&intf->dev, "Found GDM USB VID = 0x%04x PID = 0x%04x...\n",
|
||||
idVendor, idProduct);
|
||||
dev_info(&intf->dev, "GCT WiMax driver version %s\n", DRIVER_VERSION);
|
||||
|
||||
if (idProduct == EMERGENCY_PID) {
|
||||
ret = usb_emergency(usbdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Support for EEPROM bootloader */
|
||||
if (bConfigurationValue == DOWNLOAD_CONF_VALUE ||
|
||||
idProduct & B_DOWNLOAD) {
|
||||
ret = usb_boot(usbdev, bcdDevice);
|
||||
goto out;
|
||||
}
|
||||
|
||||
phy_dev = kzalloc(sizeof(*phy_dev), GFP_KERNEL);
|
||||
if (!phy_dev) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
udev = kzalloc(sizeof(*udev), GFP_KERNEL);
|
||||
if (!udev) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (idProduct == 0x7205 || idProduct == 0x7206)
|
||||
udev->padding = GDM7205_PADDING;
|
||||
else
|
||||
udev->padding = 0;
|
||||
|
||||
phy_dev->priv_dev = (void *)udev;
|
||||
phy_dev->send_func = gdm_usb_send;
|
||||
phy_dev->rcv_func = gdm_usb_receive;
|
||||
|
||||
ret = init_usb(udev);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
udev->usbdev = usbdev;
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
udev->intf = intf;
|
||||
|
||||
intf->needs_remote_wakeup = 1;
|
||||
device_init_wakeup(&intf->dev, 1);
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&usbdev->dev, 10 * 1000); /* msec */
|
||||
|
||||
INIT_WORK(&udev->pm_ws, do_pm_control);
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
|
||||
|
||||
ret = register_wimax_device(phy_dev, &intf->dev);
|
||||
if (ret)
|
||||
release_usb(udev);
|
||||
|
||||
out:
|
||||
if (ret) {
|
||||
kfree(phy_dev);
|
||||
kfree(udev);
|
||||
usb_put_dev(usbdev);
|
||||
} else {
|
||||
usb_set_intfdata(intf, phy_dev);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void gdm_usb_disconnect(struct usb_interface *intf)
|
||||
{
|
||||
u8 bConfigurationValue;
|
||||
struct phy_dev *phy_dev;
|
||||
struct usbwm_dev *udev;
|
||||
u16 idProduct;
|
||||
struct usb_device *usbdev = interface_to_usbdev(intf);
|
||||
|
||||
bConfigurationValue = usbdev->actconfig->desc.bConfigurationValue;
|
||||
phy_dev = usb_get_intfdata(intf);
|
||||
|
||||
/*USB description is set up with Little-Endian*/
|
||||
idProduct = le16_to_cpu(usbdev->descriptor.idProduct);
|
||||
|
||||
if (idProduct != EMERGENCY_PID &&
|
||||
bConfigurationValue != DOWNLOAD_CONF_VALUE &&
|
||||
(idProduct & B_DOWNLOAD) == 0) {
|
||||
udev = phy_dev->priv_dev;
|
||||
udev->usbdev = NULL;
|
||||
|
||||
unregister_wimax_device(phy_dev);
|
||||
release_usb(udev);
|
||||
kfree(udev);
|
||||
kfree(phy_dev);
|
||||
}
|
||||
|
||||
usb_put_dev(usbdev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
static int gdm_suspend(struct usb_interface *intf, pm_message_t pm_msg)
|
||||
{
|
||||
struct phy_dev *phy_dev;
|
||||
struct usbwm_dev *udev;
|
||||
struct rx_cxt *rx;
|
||||
struct usb_rx *r;
|
||||
unsigned long flags;
|
||||
|
||||
phy_dev = usb_get_intfdata(intf);
|
||||
if (!phy_dev)
|
||||
return 0;
|
||||
|
||||
udev = phy_dev->priv_dev;
|
||||
rx = &udev->rx;
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &rx->used_list, list)
|
||||
usb_unlink_urb(r->urb);
|
||||
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_resume(struct usb_interface *intf)
|
||||
{
|
||||
struct phy_dev *phy_dev;
|
||||
struct usbwm_dev *udev;
|
||||
struct rx_cxt *rx;
|
||||
struct usb_rx *r;
|
||||
unsigned long flags;
|
||||
|
||||
phy_dev = usb_get_intfdata(intf);
|
||||
if (!phy_dev)
|
||||
return 0;
|
||||
|
||||
udev = phy_dev->priv_dev;
|
||||
rx = &udev->rx;
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &rx->used_list, list)
|
||||
usb_submit_urb(r->urb, GFP_ATOMIC);
|
||||
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_USB_PM */
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
|
||||
static int k_mode_thread(void *arg)
|
||||
{
|
||||
struct usbwm_dev *udev;
|
||||
struct tx_cxt *tx;
|
||||
struct rx_cxt *rx;
|
||||
struct usb_tx *t, *temp;
|
||||
struct usb_rx *r;
|
||||
unsigned long flags, flags2, expire;
|
||||
int ret;
|
||||
|
||||
while (!k_mode_stop) {
|
||||
spin_lock_irqsave(&k_lock, flags2);
|
||||
while (!list_empty(&k_list)) {
|
||||
udev = list_entry(k_list.next, struct usbwm_dev, list);
|
||||
tx = &udev->tx;
|
||||
rx = &udev->rx;
|
||||
|
||||
list_del(&udev->list);
|
||||
spin_unlock_irqrestore(&k_lock, flags2);
|
||||
|
||||
expire = jiffies + K_WAIT_TIME;
|
||||
while (time_before(jiffies, expire))
|
||||
schedule_timeout(K_WAIT_TIME);
|
||||
|
||||
spin_lock_irqsave(&rx->lock, flags);
|
||||
|
||||
list_for_each_entry(r, &rx->used_list, list)
|
||||
usb_submit_urb(r->urb, GFP_ATOMIC);
|
||||
|
||||
spin_unlock_irqrestore(&rx->lock, flags);
|
||||
|
||||
spin_lock_irqsave(&tx->lock, flags);
|
||||
|
||||
list_for_each_entry_safe(t, temp, &tx->pending_list,
|
||||
p_list) {
|
||||
list_del(&t->p_list);
|
||||
ret = usb_submit_urb(t->urb, GFP_ATOMIC);
|
||||
|
||||
if (ret) {
|
||||
t->callback = NULL;
|
||||
__gdm_usb_send_complete(t->urb);
|
||||
}
|
||||
}
|
||||
|
||||
udev->bw_switch = 0;
|
||||
spin_unlock_irqrestore(&tx->lock, flags);
|
||||
|
||||
spin_lock_irqsave(&k_lock, flags2);
|
||||
}
|
||||
wait_event_interruptible_lock_irq(k_wait,
|
||||
!list_empty(&k_list) ||
|
||||
k_mode_stop, k_lock);
|
||||
spin_unlock_irqrestore(&k_lock, flags2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
|
||||
|
||||
static struct usb_driver gdm_usb_driver = {
|
||||
.name = "gdm_wimax",
|
||||
.probe = gdm_usb_probe,
|
||||
.disconnect = gdm_usb_disconnect,
|
||||
.id_table = id_table,
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
.supports_autosuspend = 1,
|
||||
.suspend = gdm_suspend,
|
||||
.resume = gdm_resume,
|
||||
.reset_resume = gdm_resume,
|
||||
#endif
|
||||
};
|
||||
|
||||
static int __init usb_gdm_wimax_init(void)
|
||||
{
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
|
||||
kthread_run(k_mode_thread, NULL, "k_mode_wimax");
|
||||
#endif /* CONFIG_WIMAX_GDM72XX_K_MODE */
|
||||
return usb_register(&gdm_usb_driver);
|
||||
}
|
||||
|
||||
static void __exit usb_gdm_wimax_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
|
||||
k_mode_stop = 1;
|
||||
wake_up(&k_wait);
|
||||
#endif
|
||||
usb_deregister(&gdm_usb_driver);
|
||||
}
|
||||
|
||||
module_init(usb_gdm_wimax_init);
|
||||
module_exit(usb_gdm_wimax_exit);
|
||||
|
||||
MODULE_VERSION(DRIVER_VERSION);
|
||||
MODULE_DESCRIPTION("GCT WiMax Device Driver");
|
||||
MODULE_AUTHOR("Ethan Park");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -1,78 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_GDM_USB_H__
|
||||
#define __GDM72XX_GDM_USB_H__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#define B_DIFF_DL_DRV BIT(4)
|
||||
#define B_DOWNLOAD BIT(5)
|
||||
#define MAX_NR_SDU_BUF 64
|
||||
|
||||
struct usb_tx {
|
||||
struct list_head list;
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
|
||||
struct list_head p_list;
|
||||
#endif
|
||||
struct tx_cxt *tx_cxt;
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
void (*callback)(void *cb_data);
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct tx_cxt {
|
||||
struct list_head free_list;
|
||||
struct list_head sdu_list;
|
||||
struct list_head hci_list;
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_USB_PM) || defined(CONFIG_WIMAX_GDM72XX_K_MODE)
|
||||
struct list_head pending_list;
|
||||
#endif
|
||||
spinlock_t lock; /* Protect structure fields */
|
||||
};
|
||||
|
||||
struct usb_rx {
|
||||
struct list_head list;
|
||||
struct rx_cxt *rx_cxt;
|
||||
struct urb *urb;
|
||||
u8 *buf;
|
||||
void (*callback)(void *cb_data, void *data, size_t len);
|
||||
void *cb_data;
|
||||
};
|
||||
|
||||
struct rx_cxt {
|
||||
struct list_head free_list;
|
||||
struct list_head used_list;
|
||||
spinlock_t lock; /* Protect structure fields */
|
||||
};
|
||||
|
||||
struct usbwm_dev {
|
||||
struct usb_device *usbdev;
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_USB_PM
|
||||
struct work_struct pm_ws;
|
||||
|
||||
struct usb_interface *intf;
|
||||
#endif
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_K_MODE
|
||||
int bw_switch;
|
||||
struct list_head list;
|
||||
#endif
|
||||
struct tx_cxt tx;
|
||||
struct rx_cxt rx;
|
||||
int padding;
|
||||
};
|
||||
|
||||
#endif /* __GDM72XX_GDM_USB_H__ */
|
|
@ -1,808 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/etherdevice.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/ipv6.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/in.h>
|
||||
|
||||
#include "gdm_wimax.h"
|
||||
#include "hci.h"
|
||||
#include "wm_ioctl.h"
|
||||
#include "netlink_k.h"
|
||||
|
||||
#define gdm_wimax_send(n, d, l) \
|
||||
n->phy_dev->send_func(n->phy_dev->priv_dev, d, l, NULL, NULL)
|
||||
#define gdm_wimax_send_with_cb(n, d, l, c, b) \
|
||||
n->phy_dev->send_func(n->phy_dev->priv_dev, d, l, c, b)
|
||||
#define gdm_wimax_rcv_with_cb(n, c, b) \
|
||||
n->phy_dev->rcv_func(n->phy_dev->priv_dev, c, b)
|
||||
|
||||
#define EVT_MAX_SIZE 2048
|
||||
|
||||
struct evt_entry {
|
||||
struct list_head list;
|
||||
struct net_device *dev;
|
||||
char evt_data[EVT_MAX_SIZE];
|
||||
int size;
|
||||
};
|
||||
|
||||
static struct {
|
||||
int ref_cnt;
|
||||
struct sock *sock;
|
||||
struct list_head evtq;
|
||||
spinlock_t evt_lock; /*protect structure fields */
|
||||
struct list_head freeq;
|
||||
struct work_struct ws;
|
||||
} wm_event;
|
||||
|
||||
static u8 gdm_wimax_macaddr[6] = {0x00, 0x0a, 0x3b, 0xf0, 0x01, 0x30};
|
||||
|
||||
static inline int gdm_wimax_header(struct sk_buff **pskb)
|
||||
{
|
||||
u16 buf[HCI_HEADER_SIZE / sizeof(u16)];
|
||||
struct hci_s *hci = (struct hci_s *)buf;
|
||||
struct sk_buff *skb = *pskb;
|
||||
|
||||
if (unlikely(skb_headroom(skb) < HCI_HEADER_SIZE)) {
|
||||
struct sk_buff *skb2;
|
||||
|
||||
skb2 = skb_realloc_headroom(skb, HCI_HEADER_SIZE);
|
||||
if (!skb2)
|
||||
return -ENOMEM;
|
||||
if (skb->sk)
|
||||
skb_set_owner_w(skb2, skb->sk);
|
||||
kfree_skb(skb);
|
||||
skb = skb2;
|
||||
}
|
||||
|
||||
skb_push(skb, HCI_HEADER_SIZE);
|
||||
hci->cmd_evt = cpu_to_be16(WIMAX_TX_SDU);
|
||||
hci->length = cpu_to_be16(skb->len - HCI_HEADER_SIZE);
|
||||
memcpy(skb->data, buf, HCI_HEADER_SIZE);
|
||||
|
||||
*pskb = skb;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline struct evt_entry *alloc_event_entry(void)
|
||||
{
|
||||
return kmalloc(sizeof(struct evt_entry), GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static struct evt_entry *get_event_entry(void)
|
||||
{
|
||||
struct evt_entry *e;
|
||||
|
||||
if (list_empty(&wm_event.freeq)) {
|
||||
e = alloc_event_entry();
|
||||
} else {
|
||||
e = list_entry(wm_event.freeq.next, struct evt_entry, list);
|
||||
list_del(&e->list);
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static void gdm_wimax_event_rcv(struct net_device *dev, u16 type, void *msg,
|
||||
int len)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
|
||||
u8 *buf = msg;
|
||||
u16 hci_cmd = (buf[0] << 8) | buf[1];
|
||||
u16 hci_len = (buf[2] << 8) | buf[3];
|
||||
|
||||
netdev_dbg(dev, "H=>D: 0x%04x(%d)\n", hci_cmd, hci_len);
|
||||
|
||||
gdm_wimax_send(nic, msg, len);
|
||||
}
|
||||
|
||||
static void __gdm_wimax_event_send(struct work_struct *work)
|
||||
{
|
||||
int idx;
|
||||
unsigned long flags;
|
||||
struct evt_entry *e;
|
||||
struct evt_entry *tmp;
|
||||
|
||||
spin_lock_irqsave(&wm_event.evt_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(e, tmp, &wm_event.evtq, list) {
|
||||
spin_unlock_irqrestore(&wm_event.evt_lock, flags);
|
||||
|
||||
if (sscanf(e->dev->name, "wm%d", &idx) == 1)
|
||||
netlink_send(wm_event.sock, idx, 0, e->evt_data,
|
||||
e->size);
|
||||
|
||||
spin_lock_irqsave(&wm_event.evt_lock, flags);
|
||||
list_del(&e->list);
|
||||
list_add_tail(&e->list, &wm_event.freeq);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&wm_event.evt_lock, flags);
|
||||
}
|
||||
|
||||
static int gdm_wimax_event_init(void)
|
||||
{
|
||||
if (!wm_event.ref_cnt) {
|
||||
wm_event.sock = netlink_init(NETLINK_WIMAX,
|
||||
gdm_wimax_event_rcv);
|
||||
if (wm_event.sock) {
|
||||
INIT_LIST_HEAD(&wm_event.evtq);
|
||||
INIT_LIST_HEAD(&wm_event.freeq);
|
||||
INIT_WORK(&wm_event.ws, __gdm_wimax_event_send);
|
||||
spin_lock_init(&wm_event.evt_lock);
|
||||
}
|
||||
}
|
||||
|
||||
if (wm_event.sock) {
|
||||
wm_event.ref_cnt++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
pr_err("Creating WiMax Event netlink is failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void gdm_wimax_event_exit(void)
|
||||
{
|
||||
if (wm_event.sock && --wm_event.ref_cnt == 0) {
|
||||
struct evt_entry *e, *temp;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&wm_event.evt_lock, flags);
|
||||
|
||||
list_for_each_entry_safe(e, temp, &wm_event.evtq, list) {
|
||||
list_del(&e->list);
|
||||
kfree(e);
|
||||
}
|
||||
list_for_each_entry_safe(e, temp, &wm_event.freeq, list) {
|
||||
list_del(&e->list);
|
||||
kfree(e);
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&wm_event.evt_lock, flags);
|
||||
netlink_exit(wm_event.sock);
|
||||
wm_event.sock = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gdm_wimax_event_send(struct net_device *dev, char *buf, int size)
|
||||
{
|
||||
struct evt_entry *e;
|
||||
unsigned long flags;
|
||||
|
||||
u16 hci_cmd = ((u8)buf[0] << 8) | (u8)buf[1];
|
||||
u16 hci_len = ((u8)buf[2] << 8) | (u8)buf[3];
|
||||
|
||||
netdev_dbg(dev, "D=>H: 0x%04x(%d)\n", hci_cmd, hci_len);
|
||||
|
||||
spin_lock_irqsave(&wm_event.evt_lock, flags);
|
||||
|
||||
e = get_event_entry();
|
||||
if (!e) {
|
||||
netdev_err(dev, "%s: No memory for event\n", __func__);
|
||||
spin_unlock_irqrestore(&wm_event.evt_lock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
e->dev = dev;
|
||||
e->size = size;
|
||||
memcpy(e->evt_data, buf, size);
|
||||
|
||||
list_add_tail(&e->list, &wm_event.evtq);
|
||||
spin_unlock_irqrestore(&wm_event.evt_lock, flags);
|
||||
|
||||
schedule_work(&wm_event.ws);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void tx_complete(void *arg)
|
||||
{
|
||||
struct nic *nic = arg;
|
||||
|
||||
if (netif_queue_stopped(nic->netdev))
|
||||
netif_wake_queue(nic->netdev);
|
||||
}
|
||||
|
||||
int gdm_wimax_send_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
|
||||
ret = gdm_wimax_send_with_cb(nic, skb->data, skb->len, tx_complete,
|
||||
nic);
|
||||
if (ret == -ENOSPC) {
|
||||
netif_stop_queue(dev);
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (ret) {
|
||||
skb_pull(skb, HCI_HEADER_SIZE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev->stats.tx_packets++;
|
||||
dev->stats.tx_bytes += skb->len - HCI_HEADER_SIZE;
|
||||
kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gdm_wimax_tx(struct sk_buff *skb, struct net_device *dev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
ret = gdm_wimax_header(&skb);
|
||||
if (ret < 0) {
|
||||
skb_pull(skb, HCI_HEADER_SIZE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
ret = gdm_qos_send_hci_pkt(skb, dev);
|
||||
#else
|
||||
ret = gdm_wimax_send_tx(skb, dev);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gdm_wimax_set_config(struct net_device *dev, struct ifmap *map)
|
||||
{
|
||||
if (dev->flags & IFF_UP)
|
||||
return -EBUSY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __gdm_wimax_set_mac_addr(struct net_device *dev, char *mac_addr)
|
||||
{
|
||||
u16 hci_pkt_buf[32 / sizeof(u16)];
|
||||
struct hci_s *hci = (struct hci_s *)hci_pkt_buf;
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
|
||||
/* Since dev is registered as a ethernet device,
|
||||
* ether_setup has made dev->addr_len to be ETH_ALEN
|
||||
*/
|
||||
memcpy(dev->dev_addr, mac_addr, dev->addr_len);
|
||||
|
||||
/* Let lower layer know of this change by sending
|
||||
* SetInformation(MAC Address)
|
||||
*/
|
||||
hci->cmd_evt = cpu_to_be16(WIMAX_SET_INFO);
|
||||
hci->length = cpu_to_be16(8);
|
||||
hci->data[0] = 0; /* T */
|
||||
hci->data[1] = 6; /* L */
|
||||
memcpy(&hci->data[2], mac_addr, dev->addr_len); /* V */
|
||||
|
||||
gdm_wimax_send(nic, hci, HCI_HEADER_SIZE + 8);
|
||||
}
|
||||
|
||||
/* A driver function */
|
||||
static int gdm_wimax_set_mac_addr(struct net_device *dev, void *p)
|
||||
{
|
||||
struct sockaddr *addr = p;
|
||||
|
||||
if (netif_running(dev))
|
||||
return -EBUSY;
|
||||
|
||||
if (!is_valid_ether_addr(addr->sa_data))
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
__gdm_wimax_set_mac_addr(dev, addr->sa_data);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gdm_wimax_ind_if_updown(struct net_device *dev, int if_up)
|
||||
{
|
||||
u16 buf[32 / sizeof(u16)];
|
||||
struct hci_s *hci = (struct hci_s *)buf;
|
||||
unsigned char up_down;
|
||||
|
||||
up_down = if_up ? WIMAX_IF_UP : WIMAX_IF_DOWN;
|
||||
|
||||
/* Indicate updating fsm */
|
||||
hci->cmd_evt = cpu_to_be16(WIMAX_IF_UPDOWN);
|
||||
hci->length = cpu_to_be16(sizeof(up_down));
|
||||
hci->data[0] = up_down;
|
||||
|
||||
gdm_wimax_event_send(dev, (char *)hci, HCI_HEADER_SIZE +
|
||||
sizeof(up_down));
|
||||
}
|
||||
|
||||
static int gdm_wimax_open(struct net_device *dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
struct fsm_s *fsm = nic->sdk_data[SIOC_DATA_FSM].buf;
|
||||
|
||||
netif_start_queue(dev);
|
||||
|
||||
if (fsm && fsm->m_status != M_INIT)
|
||||
gdm_wimax_ind_if_updown(dev, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_wimax_close(struct net_device *dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
struct fsm_s *fsm = nic->sdk_data[SIOC_DATA_FSM].buf;
|
||||
|
||||
netif_stop_queue(dev);
|
||||
|
||||
if (fsm && fsm->m_status != M_INIT)
|
||||
gdm_wimax_ind_if_updown(dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void kdelete(void **buf)
|
||||
{
|
||||
if (buf && *buf) {
|
||||
kfree(*buf);
|
||||
*buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static int gdm_wimax_ioctl_get_data(struct udata_s *dst, struct data_s *src)
|
||||
{
|
||||
int size;
|
||||
|
||||
size = dst->size < src->size ? dst->size : src->size;
|
||||
|
||||
dst->size = size;
|
||||
if (src->size) {
|
||||
if (!dst->buf)
|
||||
return -EINVAL;
|
||||
if (copy_to_user(dst->buf, src->buf, size))
|
||||
return -EFAULT;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_wimax_ioctl_set_data(struct data_s *dst, struct udata_s *src)
|
||||
{
|
||||
if (!src->size) {
|
||||
dst->size = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!src->buf)
|
||||
return -EINVAL;
|
||||
|
||||
if (!(dst->buf && dst->size == src->size)) {
|
||||
kdelete(&dst->buf);
|
||||
dst->buf = kmalloc(src->size, GFP_KERNEL);
|
||||
if (!dst->buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (copy_from_user(dst->buf, src->buf, src->size)) {
|
||||
kdelete(&dst->buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
dst->size = src->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gdm_wimax_cleanup_ioctl(struct net_device *dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < SIOC_DATA_MAX; i++)
|
||||
kdelete(&nic->sdk_data[i].buf);
|
||||
}
|
||||
|
||||
static void gdm_wimax_ind_fsm_update(struct net_device *dev, struct fsm_s *fsm)
|
||||
{
|
||||
u16 buf[32 / sizeof(u16)];
|
||||
struct hci_s *hci = (struct hci_s *)buf;
|
||||
|
||||
/* Indicate updating fsm */
|
||||
hci->cmd_evt = cpu_to_be16(WIMAX_FSM_UPDATE);
|
||||
hci->length = cpu_to_be16(sizeof(struct fsm_s));
|
||||
memcpy(&hci->data[0], fsm, sizeof(struct fsm_s));
|
||||
|
||||
gdm_wimax_event_send(dev, (char *)hci,
|
||||
HCI_HEADER_SIZE + sizeof(struct fsm_s));
|
||||
}
|
||||
|
||||
static void gdm_update_fsm(struct net_device *dev, struct fsm_s *new_fsm)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
struct fsm_s *cur_fsm =
|
||||
nic->sdk_data[SIOC_DATA_FSM].buf;
|
||||
|
||||
if (!cur_fsm)
|
||||
return;
|
||||
|
||||
if (cur_fsm->m_status != new_fsm->m_status ||
|
||||
cur_fsm->c_status != new_fsm->c_status) {
|
||||
if (new_fsm->m_status == M_CONNECTED) {
|
||||
netif_carrier_on(dev);
|
||||
} else if (cur_fsm->m_status == M_CONNECTED) {
|
||||
netif_carrier_off(dev);
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
gdm_qos_release_list(nic);
|
||||
#endif
|
||||
}
|
||||
gdm_wimax_ind_fsm_update(dev, new_fsm);
|
||||
}
|
||||
}
|
||||
|
||||
static int gdm_wimax_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
|
||||
{
|
||||
struct wm_req_s *req = (struct wm_req_s *)ifr;
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
int ret;
|
||||
struct fsm_s fsm_buf;
|
||||
|
||||
if (cmd != SIOCWMIOCTL)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
switch (req->cmd) {
|
||||
case SIOCG_DATA:
|
||||
case SIOCS_DATA:
|
||||
if (req->data_id >= SIOC_DATA_MAX) {
|
||||
netdev_err(dev, "%s error: data-index(%d) is invalid!!\n",
|
||||
__func__, req->data_id);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (req->cmd == SIOCG_DATA) {
|
||||
ret = gdm_wimax_ioctl_get_data(
|
||||
&req->data, &nic->sdk_data[req->data_id]);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else if (req->cmd == SIOCS_DATA) {
|
||||
if (req->data_id == SIOC_DATA_FSM) {
|
||||
/* NOTE: gdm_update_fsm should be called
|
||||
* before gdm_wimax_ioctl_set_data is called.
|
||||
*/
|
||||
if (copy_from_user(&fsm_buf, req->data.buf,
|
||||
sizeof(struct fsm_s)))
|
||||
return -EFAULT;
|
||||
|
||||
gdm_update_fsm(dev, &fsm_buf);
|
||||
}
|
||||
ret = gdm_wimax_ioctl_set_data(
|
||||
&nic->sdk_data[req->data_id], &req->data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
netdev_err(dev, "%s: %x unknown ioctl\n", __func__, cmd);
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gdm_wimax_prepare_device(struct net_device *dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
u16 buf[32 / sizeof(u16)];
|
||||
struct hci_s *hci = (struct hci_s *)buf;
|
||||
u16 len = 0;
|
||||
u32 val = 0;
|
||||
__be32 val_be32;
|
||||
|
||||
/* GetInformation mac address */
|
||||
len = 0;
|
||||
hci->cmd_evt = cpu_to_be16(WIMAX_GET_INFO);
|
||||
hci->data[len++] = TLV_T(T_MAC_ADDRESS);
|
||||
hci->length = cpu_to_be16(len);
|
||||
gdm_wimax_send(nic, hci, HCI_HEADER_SIZE + len);
|
||||
|
||||
val = T_CAPABILITY_WIMAX | T_CAPABILITY_MULTI_CS;
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
val |= T_CAPABILITY_QOS;
|
||||
#endif
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_WIMAX2)
|
||||
val |= T_CAPABILITY_AGGREGATION;
|
||||
#endif
|
||||
|
||||
/* Set capability */
|
||||
len = 0;
|
||||
hci->cmd_evt = cpu_to_be16(WIMAX_SET_INFO);
|
||||
hci->data[len++] = TLV_T(T_CAPABILITY);
|
||||
hci->data[len++] = TLV_L(T_CAPABILITY);
|
||||
val_be32 = cpu_to_be32(val);
|
||||
memcpy(&hci->data[len], &val_be32, TLV_L(T_CAPABILITY));
|
||||
len += TLV_L(T_CAPABILITY);
|
||||
hci->length = cpu_to_be16(len);
|
||||
gdm_wimax_send(nic, hci, HCI_HEADER_SIZE + len);
|
||||
|
||||
netdev_info(dev, "GDM WiMax Set CAPABILITY: 0x%08X\n", val);
|
||||
}
|
||||
|
||||
static int gdm_wimax_hci_get_tlv(u8 *buf, u8 *T, u16 *L, u8 **V)
|
||||
{
|
||||
#define __U82U16(b) ((u16)((u8 *)(b))[0] | ((u16)((u8 *)(b))[1] << 8))
|
||||
int next_pos;
|
||||
|
||||
*T = buf[0];
|
||||
if (buf[1] == 0x82) {
|
||||
*L = be16_to_cpu(__U82U16(&buf[2]));
|
||||
next_pos = 1/*type*/ + 3/*len*/;
|
||||
} else {
|
||||
*L = buf[1];
|
||||
next_pos = 1/*type*/ + 1/*len*/;
|
||||
}
|
||||
*V = &buf[next_pos];
|
||||
|
||||
next_pos += *L/*length of val*/;
|
||||
return next_pos;
|
||||
}
|
||||
|
||||
static int gdm_wimax_get_prepared_info(struct net_device *dev, char *buf,
|
||||
int len)
|
||||
{
|
||||
u8 T, *V;
|
||||
u16 L;
|
||||
u16 cmd_evt, cmd_len;
|
||||
int pos = HCI_HEADER_SIZE;
|
||||
|
||||
cmd_evt = be16_to_cpup((const __be16 *)&buf[0]);
|
||||
cmd_len = be16_to_cpup((const __be16 *)&buf[2]);
|
||||
|
||||
if (len < cmd_len + HCI_HEADER_SIZE) {
|
||||
netdev_err(dev, "%s: invalid length [%d/%d]\n", __func__,
|
||||
cmd_len + HCI_HEADER_SIZE, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cmd_evt == WIMAX_GET_INFO_RESULT) {
|
||||
if (cmd_len < 2) {
|
||||
netdev_err(dev, "%s: len is too short [%x/%d]\n",
|
||||
__func__, cmd_evt, len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
pos += gdm_wimax_hci_get_tlv(&buf[pos], &T, &L, &V);
|
||||
if (TLV_T(T_MAC_ADDRESS) == T) {
|
||||
if (dev->addr_len != L) {
|
||||
netdev_err(dev,
|
||||
"%s Invalid information result T/L [%x/%d]\n",
|
||||
__func__, T, L);
|
||||
return -1;
|
||||
}
|
||||
netdev_info(dev, "MAC change [%pM]->[%pM]\n",
|
||||
dev->dev_addr, V);
|
||||
memcpy(dev->dev_addr, V, dev->addr_len);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
gdm_wimax_event_send(dev, buf, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void gdm_wimax_netif_rx(struct net_device *dev, char *buf, int len)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
int ret;
|
||||
|
||||
skb = dev_alloc_skb(len + 2);
|
||||
if (!skb)
|
||||
return;
|
||||
skb_reserve(skb, 2);
|
||||
|
||||
dev->stats.rx_packets++;
|
||||
dev->stats.rx_bytes += len;
|
||||
|
||||
memcpy(skb_put(skb, len), buf, len);
|
||||
|
||||
skb->dev = dev;
|
||||
skb->protocol = eth_type_trans(skb, dev); /* what will happen? */
|
||||
|
||||
ret = in_interrupt() ? netif_rx(skb) : netif_rx_ni(skb);
|
||||
if (ret == NET_RX_DROP)
|
||||
netdev_err(dev, "%s skb dropped\n", __func__);
|
||||
}
|
||||
|
||||
static void gdm_wimax_transmit_aggr_pkt(struct net_device *dev, char *buf,
|
||||
int len)
|
||||
{
|
||||
#define HCI_PADDING_BYTE 4
|
||||
#define HCI_RESERVED_BYTE 4
|
||||
struct hci_s *hci;
|
||||
int length;
|
||||
|
||||
while (len > 0) {
|
||||
hci = (struct hci_s *)buf;
|
||||
|
||||
if (hci->cmd_evt != cpu_to_be16(WIMAX_RX_SDU)) {
|
||||
netdev_err(dev, "Wrong cmd_evt(0x%04X)\n",
|
||||
be16_to_cpu(hci->cmd_evt));
|
||||
break;
|
||||
}
|
||||
|
||||
length = be16_to_cpu(hci->length);
|
||||
gdm_wimax_netif_rx(dev, hci->data, length);
|
||||
|
||||
if (length & 0x3) {
|
||||
/* Add padding size */
|
||||
length += HCI_PADDING_BYTE - (length & 0x3);
|
||||
}
|
||||
|
||||
length += HCI_HEADER_SIZE + HCI_RESERVED_BYTE;
|
||||
len -= length;
|
||||
buf += length;
|
||||
}
|
||||
}
|
||||
|
||||
static void gdm_wimax_transmit_pkt(struct net_device *dev, char *buf, int len)
|
||||
{
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
struct nic *nic = netdev_priv(dev);
|
||||
#endif
|
||||
u16 cmd_evt, cmd_len;
|
||||
|
||||
/* This code is added for certain rx packet to be ignored. */
|
||||
if (len == 0)
|
||||
return;
|
||||
|
||||
cmd_evt = be16_to_cpup((const __be16 *)&buf[0]);
|
||||
cmd_len = be16_to_cpup((const __be16 *)&buf[2]);
|
||||
|
||||
if (len < cmd_len + HCI_HEADER_SIZE) {
|
||||
netdev_err(dev, "%s: invalid length [%d/%d]\n",
|
||||
__func__, cmd_len + HCI_HEADER_SIZE, len);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd_evt) {
|
||||
case WIMAX_RX_SDU_AGGR:
|
||||
gdm_wimax_transmit_aggr_pkt(dev, &buf[HCI_HEADER_SIZE],
|
||||
cmd_len);
|
||||
break;
|
||||
case WIMAX_RX_SDU:
|
||||
gdm_wimax_netif_rx(dev, &buf[HCI_HEADER_SIZE], cmd_len);
|
||||
break;
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
case WIMAX_EVT_MODEM_REPORT:
|
||||
gdm_recv_qos_hci_packet(nic, buf, len);
|
||||
break;
|
||||
#endif
|
||||
case WIMAX_SDU_TX_FLOW:
|
||||
if (buf[4] == 0) {
|
||||
if (!netif_queue_stopped(dev))
|
||||
netif_stop_queue(dev);
|
||||
} else if (buf[4] == 1) {
|
||||
if (netif_queue_stopped(dev))
|
||||
netif_wake_queue(dev);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
gdm_wimax_event_send(dev, buf, len);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void rx_complete(void *arg, void *data, size_t len)
|
||||
{
|
||||
struct nic *nic = arg;
|
||||
|
||||
gdm_wimax_transmit_pkt(nic->netdev, data, len);
|
||||
gdm_wimax_rcv_with_cb(nic, rx_complete, nic);
|
||||
}
|
||||
|
||||
static void prepare_rx_complete(void *arg, void *data, size_t len)
|
||||
{
|
||||
struct nic *nic = arg;
|
||||
int ret;
|
||||
|
||||
ret = gdm_wimax_get_prepared_info(nic->netdev, data, len);
|
||||
if (ret == 1) {
|
||||
gdm_wimax_rcv_with_cb(nic, rx_complete, nic);
|
||||
} else {
|
||||
if (ret < 0)
|
||||
netdev_err(nic->netdev,
|
||||
"get_prepared_info failed(%d)\n", ret);
|
||||
gdm_wimax_rcv_with_cb(nic, prepare_rx_complete, nic);
|
||||
}
|
||||
}
|
||||
|
||||
static void start_rx_proc(struct nic *nic)
|
||||
{
|
||||
gdm_wimax_rcv_with_cb(nic, prepare_rx_complete, nic);
|
||||
}
|
||||
|
||||
static struct net_device_ops gdm_netdev_ops = {
|
||||
.ndo_open = gdm_wimax_open,
|
||||
.ndo_stop = gdm_wimax_close,
|
||||
.ndo_set_config = gdm_wimax_set_config,
|
||||
.ndo_start_xmit = gdm_wimax_tx,
|
||||
.ndo_set_mac_address = gdm_wimax_set_mac_addr,
|
||||
.ndo_do_ioctl = gdm_wimax_ioctl,
|
||||
};
|
||||
|
||||
int register_wimax_device(struct phy_dev *phy_dev, struct device *pdev)
|
||||
{
|
||||
struct nic *nic = NULL;
|
||||
struct net_device *dev;
|
||||
int ret;
|
||||
|
||||
dev = alloc_netdev(sizeof(*nic), "wm%d", NET_NAME_UNKNOWN,
|
||||
ether_setup);
|
||||
|
||||
if (!dev) {
|
||||
pr_err("alloc_etherdev failed\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
SET_NETDEV_DEV(dev, pdev);
|
||||
dev->mtu = 1400;
|
||||
dev->netdev_ops = &gdm_netdev_ops;
|
||||
dev->flags &= ~IFF_MULTICAST;
|
||||
memcpy(dev->dev_addr, gdm_wimax_macaddr, sizeof(gdm_wimax_macaddr));
|
||||
|
||||
nic = netdev_priv(dev);
|
||||
nic->netdev = dev;
|
||||
nic->phy_dev = phy_dev;
|
||||
phy_dev->netdev = dev;
|
||||
|
||||
/* event socket init */
|
||||
ret = gdm_wimax_event_init();
|
||||
if (ret < 0) {
|
||||
pr_err("Cannot create event.\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
ret = register_netdev(dev);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
netif_carrier_off(dev);
|
||||
|
||||
#ifdef CONFIG_WIMAX_GDM72XX_QOS
|
||||
gdm_qos_init(nic);
|
||||
#endif
|
||||
|
||||
start_rx_proc(nic);
|
||||
|
||||
/* Prepare WiMax device */
|
||||
gdm_wimax_prepare_device(dev);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup:
|
||||
pr_err("register_netdev failed\n");
|
||||
free_netdev(dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void unregister_wimax_device(struct phy_dev *phy_dev)
|
||||
{
|
||||
struct nic *nic = netdev_priv(phy_dev->netdev);
|
||||
struct fsm_s *fsm = nic->sdk_data[SIOC_DATA_FSM].buf;
|
||||
|
||||
if (fsm)
|
||||
fsm->m_status = M_INIT;
|
||||
unregister_netdev(nic->netdev);
|
||||
|
||||
gdm_wimax_event_exit();
|
||||
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
gdm_qos_release_list(nic);
|
||||
#endif
|
||||
|
||||
gdm_wimax_cleanup_ioctl(phy_dev->netdev);
|
||||
|
||||
free_netdev(nic->netdev);
|
||||
}
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_GDM_WIMAX_H__
|
||||
#define __GDM72XX_GDM_WIMAX_H__
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/types.h>
|
||||
#include "wm_ioctl.h"
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
#include "gdm_qos.h"
|
||||
#endif
|
||||
|
||||
#define DRIVER_VERSION "3.2.3"
|
||||
|
||||
struct phy_dev {
|
||||
void *priv_dev;
|
||||
struct net_device *netdev;
|
||||
int (*send_func)(void *priv_dev, void *data, size_t len,
|
||||
void (*cb)(void *cb_data), void *cb_data);
|
||||
int (*rcv_func)(void *priv_dev,
|
||||
void (*cb)(void *cb_data, void *data, size_t len),
|
||||
void *cb_data);
|
||||
};
|
||||
|
||||
struct nic {
|
||||
struct net_device *netdev;
|
||||
struct phy_dev *phy_dev;
|
||||
struct data_s sdk_data[SIOC_DATA_MAX];
|
||||
#if defined(CONFIG_WIMAX_GDM72XX_QOS)
|
||||
struct qos_cb_s qos;
|
||||
#endif
|
||||
};
|
||||
|
||||
int register_wimax_device(struct phy_dev *phy_dev, struct device *pdev);
|
||||
int gdm_wimax_send_tx(struct sk_buff *skb, struct net_device *dev);
|
||||
void unregister_wimax_device(struct phy_dev *phy_dev);
|
||||
|
||||
#endif /* __GDM72XX_GDM_WIMAX_H__ */
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_HCI_H__
|
||||
#define __GDM72XX_HCI_H__
|
||||
|
||||
#define HCI_HEADER_SIZE 4
|
||||
#define HCI_VALUE_OFFS (HCI_HEADER_SIZE)
|
||||
#define HCI_MAX_PACKET 2048
|
||||
#define HCI_MAX_PARAM (HCI_MAX_PACKET - HCI_HEADER_SIZE)
|
||||
#define HCI_MAX_TLV 32
|
||||
|
||||
/* CMD-EVT */
|
||||
|
||||
/* Category 0 */
|
||||
#define WIMAX_RESET 0x0000
|
||||
#define WIMAX_SET_INFO 0x0001
|
||||
#define WIMAX_GET_INFO 0x0002
|
||||
#define WIMAX_GET_INFO_RESULT 0x8003
|
||||
#define WIMAX_RADIO_OFF 0x0004
|
||||
#define WIMAX_RADIO_ON 0x0006
|
||||
#define WIMAX_WIMAX_RESET 0x0007 /* Is this still here */
|
||||
|
||||
/* Category 1 */
|
||||
#define WIMAX_NET_ENTRY 0x0100
|
||||
#define WIMAX_NET_DISCONN 0x0102
|
||||
#define WIMAX_ENTER_SLEEP 0x0103
|
||||
#define WIMAX_EXIT_SLEEP 0x0104
|
||||
#define WIMAX_ENTER_IDLE 0x0105
|
||||
#define WIMAX_EXIT_IDLE 0x0106
|
||||
#define WIMAX_MODE_CHANGE 0x8108
|
||||
#define WIMAX_HANDOVER 0x8109 /* obsolete */
|
||||
#define WIMAX_SCAN 0x010d
|
||||
#define WIMAX_SCAN_COMPLETE 0x810e
|
||||
#define WIMAX_SCAN_RESULT 0x810f
|
||||
#define WIMAX_CONNECT 0x0110
|
||||
#define WIMAX_CONNECT_START 0x8111
|
||||
#define WIMAX_CONNECT_COMPLETE 0x8112
|
||||
#define WIMAX_ASSOC_START 0x8113
|
||||
#define WIMAX_ASSOC_COMPLETE 0x8114
|
||||
#define WIMAX_DISCONN_IND 0x8115
|
||||
#define WIMAX_ENTRY_IND 0x8116
|
||||
#define WIMAX_HO_START 0x8117
|
||||
#define WIMAX_HO_COMPLETE 0x8118
|
||||
#define WIMAX_RADIO_STATE_IND 0x8119
|
||||
#define WIMAX_IP_RENEW_IND 0x811a
|
||||
#define WIMAX_DISCOVER_NSP 0x011d
|
||||
#define WIMAX_DISCOVER_NSP_RESULT 0x811e
|
||||
#define WIMAX_SDU_TX_FLOW 0x8125
|
||||
|
||||
/* Category 2 */
|
||||
#define WIMAX_TX_EAP 0x0200
|
||||
#define WIMAX_RX_EAP 0x8201
|
||||
#define WIMAX_TX_SDU 0x0202
|
||||
#define WIMAX_RX_SDU 0x8203
|
||||
#define WIMAX_RX_SDU_AGGR 0x8204
|
||||
#define WIMAX_TX_SDU_AGGR 0x0205
|
||||
|
||||
/* Category 3 */
|
||||
#define WIMAX_DM_CMD 0x030a
|
||||
#define WIMAX_DM_RSP 0x830b
|
||||
|
||||
#define WIMAX_CLI_CMD 0x030c
|
||||
#define WIMAX_CLI_RSP 0x830d
|
||||
|
||||
#define WIMAX_DL_IMAGE 0x0310
|
||||
#define WIMAX_DL_IMAGE_STATUS 0x8311
|
||||
#define WIMAX_UL_IMAGE 0x0312
|
||||
#define WIMAX_UL_IMAGE_RESULT 0x8313
|
||||
#define WIMAX_UL_IMAGE_STATUS 0x0314
|
||||
#define WIMAX_EVT_MODEM_REPORT 0x8325
|
||||
|
||||
/* Category 0xF */
|
||||
#define WIMAX_FSM_UPDATE 0x8F01
|
||||
#define WIMAX_IF_UPDOWN 0x8F02
|
||||
#define WIMAX_IF_UP 1
|
||||
#define WIMAX_IF_DOWN 2
|
||||
|
||||
/* WIMAX mode */
|
||||
#define W_NULL 0
|
||||
#define W_STANDBY 1
|
||||
#define W_OOZ 2
|
||||
#define W_AWAKE 3
|
||||
#define W_IDLE 4
|
||||
#define W_SLEEP 5
|
||||
#define W_WAIT 6
|
||||
|
||||
#define W_NET_ENTRY_RNG 0x80
|
||||
#define W_NET_ENTRY_SBC 0x81
|
||||
#define W_NET_ENTRY_PKM 0x82
|
||||
#define W_NET_ENTRY_REG 0x83
|
||||
#define W_NET_ENTRY_DSX 0x84
|
||||
|
||||
#define W_NET_ENTRY_RNG_FAIL 0x1100100
|
||||
#define W_NET_ENTRY_SBC_FAIL 0x1100200
|
||||
#define W_NET_ENTRY_PKM_FAIL 0x1102000
|
||||
#define W_NET_ENTRY_REG_FAIL 0x1103000
|
||||
#define W_NET_ENTRY_DSX_FAIL 0x1104000
|
||||
|
||||
/* Scan Type */
|
||||
#define W_SCAN_ALL_CHANNEL 0
|
||||
#define W_SCAN_ALL_SUBSCRIPTION 1
|
||||
#define W_SCAN_SPECIFIED_SUBSCRIPTION 2
|
||||
|
||||
/* TLV
|
||||
*
|
||||
* [31:31] indicates the type is composite.
|
||||
* [30:16] is the length of the type. 0 length means length is variable.
|
||||
* [15:0] is the actual type.
|
||||
*/
|
||||
#define TLV_L(x) (((x) >> 16) & 0xff)
|
||||
#define TLV_T(x) ((x) & 0xff)
|
||||
#define TLV_COMPOSITE(x) ((x) >> 31)
|
||||
|
||||
/* GENERAL */
|
||||
#define T_MAC_ADDRESS (0x00 | (6 << 16))
|
||||
#define T_BSID (0x01 | (6 << 16))
|
||||
#define T_MSK (0x02 | (64 << 16))
|
||||
#define T_RSSI_THRSHLD (0x03 | (1 << 16))
|
||||
#define T_FREQUENCY (0x04 | (4 << 16))
|
||||
#define T_CONN_CS_TYPE (0x05 | (1 << 16))
|
||||
#define T_HOST_IP_VER (0x06 | (1 << 16))
|
||||
#define T_STBY_SCAN_INTERVAL (0x07 | (4 << 16))
|
||||
#define T_OOZ_SCAN_INTERVAL (0x08 | (4 << 16))
|
||||
#define T_IMEI (0x09 | (8 << 16))
|
||||
#define T_PID (0x0a | (12 << 16))
|
||||
#define T_CAPABILITY (0x1a | (4 << 16))
|
||||
#define T_RELEASE_NUMBER (0x1b | (4 << 16))
|
||||
#define T_DRIVER_REVISION (0x1c | (4 << 16))
|
||||
#define T_FW_REVISION (0x1d | (4 << 16))
|
||||
#define T_MAC_HW_REVISION (0x1e | (4 << 16))
|
||||
#define T_PHY_HW_REVISION (0x1f | (4 << 16))
|
||||
|
||||
/* HANDOVER */
|
||||
#define T_SCAN_INTERVAL (0x20 | (1 << 16))
|
||||
#define T_RSC_RETAIN_TIME (0x2f | (2 << 16))
|
||||
|
||||
/* SLEEP */
|
||||
#define T_TYPE1_ISW (0x40 | (1 << 16))
|
||||
#define T_SLP_START_TO (0x4a | (2 << 16))
|
||||
|
||||
/* IDLE */
|
||||
#define T_IDLE_MODE_TO (0x50 | (2 << 16))
|
||||
#define T_IDLE_START_TO (0x54 | (2 << 16))
|
||||
|
||||
/* MONITOR */
|
||||
#define T_RSSI (0x60 | (1 << 16))
|
||||
#define T_CINR (0x61 | (1 << 16))
|
||||
#define T_TX_POWER (0x6a | (1 << 16))
|
||||
#define T_CUR_FREQ (0x7f | (4 << 16))
|
||||
|
||||
/* WIMAX */
|
||||
#define T_MAX_SUBSCRIPTION (0xa1 | (1 << 16))
|
||||
#define T_MAX_SF (0xa2 | (1 << 16))
|
||||
#define T_PHY_TYPE (0xa3 | (1 << 16))
|
||||
#define T_PKM (0xa4 | (1 << 16))
|
||||
#define T_AUTH_POLICY (0xa5 | (1 << 16))
|
||||
#define T_CS_TYPE (0xa6 | (2 << 16))
|
||||
#define T_VENDOR_NAME (0xa7 | (0 << 16))
|
||||
#define T_MOD_NAME (0xa8 | (0 << 16))
|
||||
#define T_PACKET_FILTER (0xa9 | (1 << 16))
|
||||
#define T_NSP_CHANGE_COUNT (0xaa | (4 << 16))
|
||||
#define T_RADIO_STATE (0xab | (1 << 16))
|
||||
#define T_URI_CONTACT_TYPE (0xac | (1 << 16))
|
||||
#define T_URI_TEXT (0xad | (0 << 16))
|
||||
#define T_URI (0xae | (0 << 16))
|
||||
#define T_ENABLE_AUTH (0xaf | (1 << 16))
|
||||
#define T_TIMEOUT (0xb0 | (2 << 16))
|
||||
#define T_RUN_MODE (0xb1 | (1 << 16))
|
||||
#define T_OMADMT_VER (0xb2 | (4 << 16))
|
||||
/* This is measured in seconds from 00:00:00 GMT January 1, 1970. */
|
||||
#define T_RTC_TIME (0xb3 | (4 << 16))
|
||||
#define T_CERT_STATUS (0xb4 | (4 << 16))
|
||||
#define T_CERT_MASK (0xb5 | (4 << 16))
|
||||
#define T_EMSK (0xb6 | (64 << 16))
|
||||
|
||||
/* Subscription TLV */
|
||||
#define T_SUBSCRIPTION_LIST (0xd1 | (0 << 16) | (1 << 31))
|
||||
#define T_H_NSPID (0xd2 | (3 << 16))
|
||||
#define T_NSP_NAME (0xd3 | (0 << 16))
|
||||
#define T_SUBSCRIPTION_NAME (0xd4 | (0 << 16))
|
||||
#define T_SUBSCRIPTION_FLAG (0xd5 | (2 << 16))
|
||||
#define T_V_NSPID (0xd6 | (3 << 16))
|
||||
#define T_NAP_ID (0xd7 | (3 << 16))
|
||||
#define T_PREAMBLES (0xd8 | (15 << 16))
|
||||
#define T_BW (0xd9 | (4 << 16))
|
||||
#define T_FFTSIZE (0xda | (4 << 16))
|
||||
#define T_DUPLEX_MODE (0xdb | (4 << 16))
|
||||
|
||||
/* T_CAPABILITY */
|
||||
#define T_CAPABILITY_MULTI_CS BIT(0)
|
||||
#define T_CAPABILITY_WIMAX BIT(1)
|
||||
#define T_CAPABILITY_QOS BIT(2)
|
||||
#define T_CAPABILITY_AGGREGATION BIT(3)
|
||||
|
||||
struct hci_s {
|
||||
__be16 cmd_evt;
|
||||
__be16 length;
|
||||
u8 data[0];
|
||||
} __packed;
|
||||
|
||||
#endif /* __GDM72XX_HCI_H__ */
|
|
@ -1,156 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <net/netlink.h>
|
||||
#include <asm/byteorder.h>
|
||||
#include <net/sock.h>
|
||||
#include "netlink_k.h"
|
||||
|
||||
#if !defined(NLMSG_HDRLEN)
|
||||
#define NLMSG_HDRLEN ((int)NLMSG_ALIGN(sizeof(struct nlmsghdr)))
|
||||
#endif
|
||||
|
||||
#define ND_MAX_GROUP 30
|
||||
#define ND_IFINDEX_LEN sizeof(int)
|
||||
#define ND_NLMSG_SPACE(len) (nlmsg_total_size(len) + ND_IFINDEX_LEN)
|
||||
#define ND_NLMSG_DATA(nlh) \
|
||||
((void *)((char *)nlmsg_data(nlh) + ND_IFINDEX_LEN))
|
||||
#define ND_NLMSG_S_LEN(len) (len + ND_IFINDEX_LEN)
|
||||
#define ND_NLMSG_R_LEN(nlh) (nlh->nlmsg_len - ND_IFINDEX_LEN)
|
||||
#define ND_NLMSG_IFIDX(nlh) nlmsg_data(nlh)
|
||||
#define ND_MAX_MSG_LEN 8096
|
||||
|
||||
#if defined(DEFINE_MUTEX)
|
||||
static DEFINE_MUTEX(netlink_mutex);
|
||||
#else
|
||||
static struct semaphore netlink_mutex;
|
||||
#define mutex_lock(x) down(x)
|
||||
#define mutex_unlock(x) up(x)
|
||||
#endif
|
||||
|
||||
static void (*rcv_cb)(struct net_device *dev, u16 type, void *msg, int len);
|
||||
|
||||
static void netlink_rcv_cb(struct sk_buff *skb)
|
||||
{
|
||||
struct nlmsghdr *nlh;
|
||||
struct net_device *dev;
|
||||
u32 mlen;
|
||||
void *msg;
|
||||
int ifindex;
|
||||
|
||||
if (skb->len >= NLMSG_HDRLEN) {
|
||||
nlh = (struct nlmsghdr *)skb->data;
|
||||
|
||||
if (nlh->nlmsg_len < ND_IFINDEX_LEN ||
|
||||
nlh->nlmsg_len > skb->len ||
|
||||
nlh->nlmsg_len > ND_MAX_MSG_LEN) {
|
||||
netdev_err(skb->dev, "Invalid length (%d,%d)\n",
|
||||
skb->len, nlh->nlmsg_len);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(&ifindex, ND_NLMSG_IFIDX(nlh), ND_IFINDEX_LEN);
|
||||
msg = ND_NLMSG_DATA(nlh);
|
||||
mlen = ND_NLMSG_R_LEN(nlh);
|
||||
|
||||
if (rcv_cb) {
|
||||
dev = dev_get_by_index(&init_net, ifindex);
|
||||
if (dev) {
|
||||
rcv_cb(dev, nlh->nlmsg_type, msg, mlen);
|
||||
dev_put(dev);
|
||||
} else
|
||||
netdev_err(skb->dev,
|
||||
"dev_get_by_index(%d) is not found.\n",
|
||||
ifindex);
|
||||
} else {
|
||||
netdev_err(skb->dev, "Unregistered Callback\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void netlink_rcv(struct sk_buff *skb)
|
||||
{
|
||||
mutex_lock(&netlink_mutex);
|
||||
netlink_rcv_cb(skb);
|
||||
mutex_unlock(&netlink_mutex);
|
||||
}
|
||||
|
||||
struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type,
|
||||
void *msg, int len))
|
||||
{
|
||||
struct sock *sock;
|
||||
struct netlink_kernel_cfg cfg = {
|
||||
.input = netlink_rcv,
|
||||
};
|
||||
|
||||
#if !defined(DEFINE_MUTEX)
|
||||
init_MUTEX(&netlink_mutex);
|
||||
#endif
|
||||
|
||||
sock = netlink_kernel_create(&init_net, unit, &cfg);
|
||||
|
||||
if (sock)
|
||||
rcv_cb = cb;
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
void netlink_exit(struct sock *sock)
|
||||
{
|
||||
netlink_kernel_release(sock);
|
||||
}
|
||||
|
||||
int netlink_send(struct sock *sock, u16 group, u16 type, void *msg, int len)
|
||||
{
|
||||
static u32 seq;
|
||||
struct sk_buff *skb = NULL;
|
||||
struct nlmsghdr *nlh;
|
||||
int ret = 0;
|
||||
|
||||
if (group > ND_MAX_GROUP) {
|
||||
pr_err("Group %d is invalid.\n", group);
|
||||
pr_err("Valid group is 0 ~ %d.\n", ND_MAX_GROUP);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
skb = nlmsg_new(len, GFP_ATOMIC);
|
||||
if (!skb) {
|
||||
pr_err("netlink_broadcast ret=%d\n", ret);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
seq++;
|
||||
nlh = nlmsg_put(skb, 0, seq, type, len, 0);
|
||||
if (!nlh) {
|
||||
kfree_skb(skb);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
memcpy(nlmsg_data(nlh), msg, len);
|
||||
|
||||
NETLINK_CB(skb).portid = 0;
|
||||
NETLINK_CB(skb).dst_group = 0;
|
||||
|
||||
ret = netlink_broadcast(sock, skb, 0, group + 1, GFP_ATOMIC);
|
||||
|
||||
if (!ret)
|
||||
return len;
|
||||
if (ret != -ESRCH) {
|
||||
pr_err("netlink_broadcast g=%d, t=%d, l=%d, r=%d\n",
|
||||
group, type, len, ret);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_NETLINK_K_H__
|
||||
#define __GDM72XX_NETLINK_K_H__
|
||||
|
||||
#include <linux/netdevice.h>
|
||||
#include <net/sock.h>
|
||||
|
||||
struct sock *netlink_init(int unit, void (*cb)(struct net_device *dev, u16 type,
|
||||
void *msg, int len));
|
||||
void netlink_exit(struct sock *sock);
|
||||
int netlink_send(struct sock *sock, u16 group, u16 type, void *msg, int len);
|
||||
|
||||
#endif /* __GDM72XX_NETLINK_K_H__ */
|
|
@ -1,158 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/mmc/core.h>
|
||||
#include <linux/mmc/card.h>
|
||||
#include <linux/mmc/sdio_func.h>
|
||||
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include "gdm_sdio.h"
|
||||
#include "sdio_boot.h"
|
||||
|
||||
#define TYPE_A_HEADER_SIZE 4
|
||||
#define TYPE_A_LOOKAHEAD_SIZE 16
|
||||
#define YMEM0_SIZE 0x8000 /* 32kbytes */
|
||||
#define DOWNLOAD_SIZE (YMEM0_SIZE - TYPE_A_HEADER_SIZE)
|
||||
|
||||
#define FW_DIR "gdm72xx/"
|
||||
#define FW_KRN "gdmskrn.bin"
|
||||
#define FW_RFS "gdmsrfs.bin"
|
||||
|
||||
static u8 *tx_buf;
|
||||
|
||||
static int ack_ready(struct sdio_func *func)
|
||||
{
|
||||
unsigned long wait = jiffies + HZ;
|
||||
u8 val;
|
||||
int ret;
|
||||
|
||||
while (time_before(jiffies, wait)) {
|
||||
val = sdio_readb(func, 0x13, &ret);
|
||||
if (val & 0x01)
|
||||
return 1;
|
||||
schedule();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int download_image(struct sdio_func *func, const char *img_name)
|
||||
{
|
||||
int ret = 0, len, pno;
|
||||
u8 *buf = tx_buf;
|
||||
loff_t pos = 0;
|
||||
int img_len;
|
||||
const struct firmware *firm;
|
||||
|
||||
ret = request_firmware(&firm, img_name, &func->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&func->dev,
|
||||
"requesting firmware %s failed with error %d\n",
|
||||
img_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf = kmalloc(DOWNLOAD_SIZE + TYPE_A_HEADER_SIZE, GFP_KERNEL);
|
||||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
img_len = firm->size;
|
||||
|
||||
if (img_len <= 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pno = 0;
|
||||
while (img_len > 0) {
|
||||
if (img_len > DOWNLOAD_SIZE) {
|
||||
len = DOWNLOAD_SIZE;
|
||||
buf[3] = 0;
|
||||
} else {
|
||||
len = img_len; /* the last packet */
|
||||
buf[3] = 2;
|
||||
}
|
||||
|
||||
buf[0] = len & 0xff;
|
||||
buf[1] = (len >> 8) & 0xff;
|
||||
buf[2] = (len >> 16) & 0xff;
|
||||
|
||||
memcpy(buf + TYPE_A_HEADER_SIZE, firm->data + pos, len);
|
||||
ret = sdio_memcpy_toio(func, 0, buf, len + TYPE_A_HEADER_SIZE);
|
||||
if (ret < 0) {
|
||||
dev_err(&func->dev,
|
||||
"send image error: packet number = %d ret = %d\n",
|
||||
pno, ret);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (buf[3] == 2) /* The last packet */
|
||||
break;
|
||||
if (!ack_ready(func)) {
|
||||
ret = -EIO;
|
||||
dev_err(&func->dev, "Ack is not ready.\n");
|
||||
goto out;
|
||||
}
|
||||
ret = sdio_memcpy_fromio(func, buf, 0, TYPE_A_LOOKAHEAD_SIZE);
|
||||
if (ret < 0) {
|
||||
dev_err(&func->dev,
|
||||
"receive ack error: packet number = %d ret = %d\n",
|
||||
pno, ret);
|
||||
goto out;
|
||||
}
|
||||
sdio_writeb(func, 0x01, 0x13, &ret);
|
||||
sdio_writeb(func, 0x00, 0x10, &ret); /* PCRRT */
|
||||
|
||||
img_len -= DOWNLOAD_SIZE;
|
||||
pos += DOWNLOAD_SIZE;
|
||||
pno++;
|
||||
}
|
||||
|
||||
out:
|
||||
kfree(buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sdio_boot(struct sdio_func *func)
|
||||
{
|
||||
int ret;
|
||||
const char *krn_name = FW_DIR FW_KRN;
|
||||
const char *rfs_name = FW_DIR FW_RFS;
|
||||
|
||||
tx_buf = kmalloc(YMEM0_SIZE, GFP_KERNEL);
|
||||
if (!tx_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
ret = download_image(func, krn_name);
|
||||
if (ret)
|
||||
goto restore_fs;
|
||||
dev_info(&func->dev, "GCT: Kernel download success.\n");
|
||||
|
||||
ret = download_image(func, rfs_name);
|
||||
if (ret)
|
||||
goto restore_fs;
|
||||
dev_info(&func->dev, "GCT: Filesystem download success.\n");
|
||||
|
||||
restore_fs:
|
||||
kfree(tx_buf);
|
||||
return ret;
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_SDIO_BOOT_H__
|
||||
#define __GDM72XX_SDIO_BOOT_H__
|
||||
|
||||
struct sdio_func;
|
||||
|
||||
int sdio_boot(struct sdio_func *func);
|
||||
|
||||
#endif /* __GDM72XX_SDIO_BOOT_H__ */
|
|
@ -1,361 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/usb.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
#include "gdm_usb.h"
|
||||
#include "usb_boot.h"
|
||||
|
||||
#define DN_KERNEL_MAGIC_NUMBER 0x10760001
|
||||
#define DN_ROOTFS_MAGIC_NUMBER 0x10760002
|
||||
|
||||
#define DOWNLOAD_SIZE 1024
|
||||
|
||||
#define MAX_IMG_CNT 16
|
||||
#define FW_DIR "gdm72xx/"
|
||||
#define FW_UIMG "gdmuimg.bin"
|
||||
#define FW_KERN "zImage"
|
||||
#define FW_FS "ramdisk.jffs2"
|
||||
|
||||
struct dn_header {
|
||||
__be32 magic_num;
|
||||
__be32 file_size;
|
||||
};
|
||||
|
||||
struct img_header {
|
||||
u32 magic_code;
|
||||
u32 count;
|
||||
u32 len;
|
||||
u32 offset[MAX_IMG_CNT];
|
||||
char hostname[32];
|
||||
char date[32];
|
||||
};
|
||||
|
||||
struct fw_info {
|
||||
u32 id;
|
||||
u32 len;
|
||||
u32 kernel_len;
|
||||
u32 rootfs_len;
|
||||
u32 kernel_offset;
|
||||
u32 rootfs_offset;
|
||||
u32 fw_ver;
|
||||
u32 mac_ver;
|
||||
char hostname[32];
|
||||
char userid[16];
|
||||
char date[32];
|
||||
char user_desc[128];
|
||||
};
|
||||
|
||||
static void array_le32_to_cpu(u32 *arr, int num)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < num; i++, arr++)
|
||||
le32_to_cpus(arr);
|
||||
}
|
||||
|
||||
static u8 *tx_buf;
|
||||
|
||||
static int gdm_wibro_send(struct usb_device *usbdev, void *data, int len)
|
||||
{
|
||||
int ret;
|
||||
int actual;
|
||||
|
||||
ret = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), data, len,
|
||||
&actual, 1000);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&usbdev->dev, "Error : usb_bulk_msg ( result = %d )\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gdm_wibro_recv(struct usb_device *usbdev, void *data, int len)
|
||||
{
|
||||
int ret;
|
||||
int actual;
|
||||
|
||||
ret = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), data, len,
|
||||
&actual, 5000);
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(&usbdev->dev,
|
||||
"Error : usb_bulk_msg(recv) ( result = %d )\n", ret);
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int download_image(struct usb_device *usbdev,
|
||||
const struct firmware *firm,
|
||||
loff_t pos, u32 img_len, u32 magic_num)
|
||||
{
|
||||
struct dn_header h;
|
||||
int ret = 0;
|
||||
u32 size;
|
||||
|
||||
size = ALIGN(img_len, DOWNLOAD_SIZE);
|
||||
h.magic_num = cpu_to_be32(magic_num);
|
||||
h.file_size = cpu_to_be32(size);
|
||||
|
||||
ret = gdm_wibro_send(usbdev, &h, sizeof(h));
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
while (img_len > 0) {
|
||||
if (img_len > DOWNLOAD_SIZE)
|
||||
size = DOWNLOAD_SIZE;
|
||||
else
|
||||
size = img_len; /* the last chunk of data */
|
||||
|
||||
memcpy(tx_buf, firm->data + pos, size);
|
||||
ret = gdm_wibro_send(usbdev, tx_buf, size);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
img_len -= size;
|
||||
pos += size;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int usb_boot(struct usb_device *usbdev, u16 pid)
|
||||
{
|
||||
int i, ret = 0;
|
||||
struct img_header hdr;
|
||||
struct fw_info fw_info;
|
||||
loff_t pos = 0;
|
||||
char *img_name = FW_DIR FW_UIMG;
|
||||
const struct firmware *firm;
|
||||
|
||||
ret = request_firmware(&firm, img_name, &usbdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&usbdev->dev,
|
||||
"requesting firmware %s failed with error %d\n",
|
||||
img_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tx_buf = kmalloc(DOWNLOAD_SIZE, GFP_KERNEL);
|
||||
if (!tx_buf) {
|
||||
release_firmware(firm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (firm->size < sizeof(hdr)) {
|
||||
dev_err(&usbdev->dev, "Cannot read the image info.\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
memcpy(&hdr, firm->data, sizeof(hdr));
|
||||
|
||||
array_le32_to_cpu((u32 *)&hdr, 19);
|
||||
|
||||
if (hdr.count > MAX_IMG_CNT) {
|
||||
dev_err(&usbdev->dev, "Too many images. %d\n", hdr.count);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (i = 0; i < hdr.count; i++) {
|
||||
if (hdr.offset[i] > hdr.len) {
|
||||
dev_err(&usbdev->dev,
|
||||
"Invalid offset. Entry = %d Offset = 0x%08x Image length = 0x%08x\n",
|
||||
i, hdr.offset[i], hdr.len);
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
pos = hdr.offset[i];
|
||||
if (firm->size < sizeof(fw_info) + pos) {
|
||||
dev_err(&usbdev->dev, "Cannot read the FW info.\n");
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
memcpy(&fw_info, firm->data + pos, sizeof(fw_info));
|
||||
|
||||
array_le32_to_cpu((u32 *)&fw_info, 8);
|
||||
|
||||
if ((fw_info.id & 0xffff) != pid)
|
||||
continue;
|
||||
|
||||
pos = hdr.offset[i] + fw_info.kernel_offset;
|
||||
if (firm->size < fw_info.kernel_len + pos) {
|
||||
dev_err(&usbdev->dev, "Kernel FW is too small.\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = download_image(usbdev, firm, pos, fw_info.kernel_len,
|
||||
DN_KERNEL_MAGIC_NUMBER);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
dev_info(&usbdev->dev, "GCT: Kernel download success.\n");
|
||||
|
||||
pos = hdr.offset[i] + fw_info.rootfs_offset;
|
||||
if (firm->size < fw_info.rootfs_len + pos) {
|
||||
dev_err(&usbdev->dev, "Filesystem FW is too small.\n");
|
||||
goto out;
|
||||
}
|
||||
ret = download_image(usbdev, firm, pos, fw_info.rootfs_len,
|
||||
DN_ROOTFS_MAGIC_NUMBER);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
dev_info(&usbdev->dev, "GCT: Filesystem download success.\n");
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (i == hdr.count) {
|
||||
dev_err(&usbdev->dev, "Firmware for gsk%x is not installed.\n",
|
||||
pid);
|
||||
ret = -EINVAL;
|
||||
}
|
||||
out:
|
||||
release_firmware(firm);
|
||||
kfree(tx_buf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*#define GDM7205_PADDING 256 */
|
||||
#define DOWNLOAD_CHUCK 2048
|
||||
#define KERNEL_TYPE_STRING "linux"
|
||||
#define FS_TYPE_STRING "rootfs"
|
||||
|
||||
static int em_wait_ack(struct usb_device *usbdev, int send_zlp)
|
||||
{
|
||||
int ack;
|
||||
int ret = -1;
|
||||
|
||||
if (send_zlp) {
|
||||
/*Send ZLP*/
|
||||
ret = gdm_wibro_send(usbdev, NULL, 0);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*Wait for ACK*/
|
||||
ret = gdm_wibro_recv(usbdev, &ack, sizeof(ack));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int em_download_image(struct usb_device *usbdev, const char *img_name,
|
||||
char *type_string)
|
||||
{
|
||||
char *buf = NULL;
|
||||
loff_t pos = 0;
|
||||
int ret = 0;
|
||||
int len;
|
||||
int img_len;
|
||||
const struct firmware *firm;
|
||||
#if defined(GDM7205_PADDING)
|
||||
const int pad_size = GDM7205_PADDING;
|
||||
#else
|
||||
const int pad_size = 0;
|
||||
#endif
|
||||
|
||||
ret = request_firmware(&firm, img_name, &usbdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&usbdev->dev,
|
||||
"requesting firmware %s failed with error %d\n",
|
||||
img_name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
buf = kzalloc(DOWNLOAD_CHUCK + pad_size, GFP_KERNEL);
|
||||
if (!buf) {
|
||||
release_firmware(firm);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
strcpy(buf + pad_size, type_string);
|
||||
ret = gdm_wibro_send(usbdev, buf, strlen(type_string) + pad_size);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
img_len = firm->size;
|
||||
|
||||
if (img_len <= 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
while (img_len > 0) {
|
||||
if (img_len > DOWNLOAD_CHUCK)
|
||||
len = DOWNLOAD_CHUCK;
|
||||
else
|
||||
len = img_len; /* the last chunk of data */
|
||||
|
||||
memcpy(buf + pad_size, firm->data + pos, len);
|
||||
ret = gdm_wibro_send(usbdev, buf, len + pad_size);
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
img_len -= DOWNLOAD_CHUCK;
|
||||
pos += DOWNLOAD_CHUCK;
|
||||
|
||||
ret = em_wait_ack(usbdev, ((len + pad_size) % 512 == 0));
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = em_wait_ack(usbdev, 1);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
release_firmware(firm);
|
||||
kfree(buf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int em_fw_reset(struct usb_device *usbdev)
|
||||
{
|
||||
/*Send ZLP*/
|
||||
return gdm_wibro_send(usbdev, NULL, 0);
|
||||
}
|
||||
|
||||
int usb_emergency(struct usb_device *usbdev)
|
||||
{
|
||||
int ret;
|
||||
const char *kern_name = FW_DIR FW_KERN;
|
||||
const char *fs_name = FW_DIR FW_FS;
|
||||
|
||||
ret = em_download_image(usbdev, kern_name, KERNEL_TYPE_STRING);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dev_err(&usbdev->dev, "GCT Emergency: Kernel download success.\n");
|
||||
|
||||
ret = em_download_image(usbdev, fs_name, FS_TYPE_STRING);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
dev_info(&usbdev->dev, "GCT Emergency: Filesystem download success.\n");
|
||||
|
||||
return em_fw_reset(usbdev);
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_USB_BOOT_H__
|
||||
#define __GDM72XX_USB_BOOT_H__
|
||||
|
||||
struct usb_device;
|
||||
|
||||
int usb_boot(struct usb_device *usbdev, u16 pid);
|
||||
int usb_emergency(struct usb_device *usbdev);
|
||||
|
||||
#endif /* __GDM72XX_USB_BOOT_H__ */
|
|
@ -1,86 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_USB_IDS_H__
|
||||
#define __GDM72XX_USB_IDS_H__
|
||||
|
||||
/*You can replace vendor-ID as yours.*/
|
||||
#define GCT_VID 0x1076
|
||||
|
||||
/*You can replace product-ID as yours.*/
|
||||
#define GCT_PID1 0x7e00
|
||||
#define GCT_PID2 0x7f00
|
||||
|
||||
#define USB_DEVICE_ID_MATCH_DEVICE_INTERFACE \
|
||||
(USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_CLASS)
|
||||
|
||||
#define USB_DEVICE_INTF(vend, prod, intf) \
|
||||
.match_flags = USB_DEVICE_ID_MATCH_DEVICE_INTERFACE, \
|
||||
.idVendor = (vend), .idProduct = (prod), .bInterfaceClass = (intf)
|
||||
|
||||
#define EMERGENCY_PID 0x720f
|
||||
#define BL_PID_MASK 0xffc0
|
||||
|
||||
#define USB_DEVICE_BOOTLOADER(vid, pid) \
|
||||
{USB_DEVICE((vid), ((pid) & BL_PID_MASK) | B_DOWNLOAD)}
|
||||
|
||||
#define USB_DEVICE_BOOTLOADER_DRV(vid, pid) \
|
||||
{USB_DEVICE((vid), ((pid) & BL_PID_MASK) | B_DOWNLOAD | B_DIFF_DL_DRV)}
|
||||
|
||||
#define USB_DEVICE_CDC_DATA(vid, pid) \
|
||||
{USB_DEVICE_INTF((vid), (pid), USB_CLASS_CDC_DATA)}
|
||||
|
||||
static const struct usb_device_id id_table[] = {
|
||||
USB_DEVICE_BOOTLOADER(GCT_VID, GCT_PID1),
|
||||
USB_DEVICE_BOOTLOADER_DRV(GCT_VID, GCT_PID1),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x1),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x2),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x3),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x4),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x5),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x6),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x7),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x8),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0x9),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0xa),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0xb),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0xc),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0xd),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0xe),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID1 + 0xf),
|
||||
|
||||
USB_DEVICE_BOOTLOADER(GCT_VID, GCT_PID2),
|
||||
USB_DEVICE_BOOTLOADER_DRV(GCT_VID, GCT_PID2),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x1),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x2),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x3),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x4),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x5),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x6),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x7),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x8),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0x9),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0xa),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0xb),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0xc),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0xd),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0xe),
|
||||
USB_DEVICE_CDC_DATA(GCT_VID, GCT_PID2 + 0xf),
|
||||
|
||||
{USB_DEVICE(GCT_VID, EMERGENCY_PID)},
|
||||
{ }
|
||||
};
|
||||
|
||||
#endif /* __GDM72XX_USB_IDS_H__ */
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2012 GCT Semiconductor, Inc. All rights reserved.
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
* License version 2, as published by the Free Software Foundation, and
|
||||
* may be copied, distributed, and modified under those terms.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __GDM72XX_WM_IOCTL_H__
|
||||
#define __GDM72XX_WM_IOCTL_H__
|
||||
|
||||
#if !defined(__KERNEL__)
|
||||
#include <net/if.h>
|
||||
#endif
|
||||
|
||||
#define NETLINK_WIMAX 31
|
||||
|
||||
#define SIOCWMIOCTL SIOCDEVPRIVATE
|
||||
|
||||
#define SIOCG_DATA 0x8D10
|
||||
#define SIOCS_DATA 0x8D11
|
||||
|
||||
enum {
|
||||
SIOC_DATA_FSM,
|
||||
SIOC_DATA_NETLIST,
|
||||
SIOC_DATA_CONNNSP,
|
||||
SIOC_DATA_CONNCOMP,
|
||||
SIOC_DATA_PROFILEID,
|
||||
|
||||
SIOC_DATA_END
|
||||
};
|
||||
|
||||
#define SIOC_DATA_MAX 16
|
||||
|
||||
/* FSM */
|
||||
enum {
|
||||
M_INIT = 0,
|
||||
M_OPEN_OFF,
|
||||
M_OPEN_ON,
|
||||
M_SCAN,
|
||||
M_CONNECTING,
|
||||
M_CONNECTED,
|
||||
M_FSM_END,
|
||||
|
||||
C_INIT = 0,
|
||||
C_CONNSTART,
|
||||
C_ASSOCSTART,
|
||||
C_RNG,
|
||||
C_SBC,
|
||||
C_AUTH,
|
||||
C_REG,
|
||||
C_DSX,
|
||||
C_ASSOCCOMPLETE,
|
||||
C_CONNCOMPLETE,
|
||||
C_FSM_END,
|
||||
|
||||
D_INIT = 0,
|
||||
D_READY,
|
||||
D_LISTEN,
|
||||
D_IPACQUISITION,
|
||||
|
||||
END_FSM
|
||||
};
|
||||
|
||||
struct fsm_s {
|
||||
int m_status; /*main status*/
|
||||
int c_status; /*connection status*/
|
||||
int d_status; /*oma-dm status*/
|
||||
};
|
||||
|
||||
struct data_s {
|
||||
unsigned int size;
|
||||
void *buf;
|
||||
};
|
||||
|
||||
struct udata_s {
|
||||
unsigned int size;
|
||||
void __user *buf;
|
||||
};
|
||||
|
||||
struct wm_req_s {
|
||||
union {
|
||||
char ifrn_name[IFNAMSIZ];
|
||||
} ifr_ifrn;
|
||||
unsigned short cmd;
|
||||
unsigned short data_id;
|
||||
struct udata_s data;
|
||||
|
||||
/* NOTE: sizeof(struct wm_req_s) must be less than sizeof(struct ifreq). */
|
||||
};
|
||||
|
||||
#ifndef ifr_name
|
||||
#define ifr_name ifr_ifrn.ifrn_name
|
||||
#endif
|
||||
|
||||
#endif /* __GDM72XX_WM_IOCTL_H__ */
|
Загрузка…
Ссылка в новой задаче