/* * 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 void __gdm_wimax_event_send(struct work_struct *work); static inline struct evt_entry *alloc_event_entry(void); static inline void free_event_entry(struct evt_entry *e); static struct evt_entry *get_event_entry(void); static void put_event_entry(struct evt_entry *e); static struct { int ref_cnt; struct sock *sock; struct list_head evtq; spinlock_t evt_lock; struct list_head freeq; struct work_struct ws; } wm_event; static u8 gdm_wimax_macaddr[6] = {0x00, 0x0a, 0x3b, 0xf0, 0x01, 0x30}; static void gdm_wimax_ind_fsm_update(struct net_device *dev, struct fsm_s *fsm); static void gdm_wimax_ind_if_updown(struct net_device *dev, int if_up); #if defined(DEBUG_SDU) static void printk_hex(u8 *buf, u32 size) { int i; for (i = 0; i < size; i++) { if (i && i % 16 == 0) printk(KERN_DEBUG "\n%02x ", *buf++); else printk(KERN_DEBUG "%02x ", *buf++); } printk(KERN_DEBUG "\n"); } static const char *get_protocol_name(u16 protocol) { static char buf[32]; const char *name = "-"; switch (protocol) { case ETH_P_ARP: name = "ARP"; break; case ETH_P_IP: name = "IP"; break; case ETH_P_IPV6: name = "IPv6"; break; } sprintf(buf, "0x%04x(%s)", protocol, name); return buf; } static const char *get_ip_protocol_name(u8 ip_protocol) { static char buf[32]; const char *name = "-"; switch (ip_protocol) { case IPPROTO_TCP: name = "TCP"; break; case IPPROTO_UDP: name = "UDP"; break; case IPPROTO_ICMP: name = "ICMP"; break; } sprintf(buf, "%u(%s)", ip_protocol, name); return buf; } static const char *get_port_name(u16 port) { static char buf[32]; const char *name = "-"; switch (port) { case 67: name = "DHCP-Server"; break; case 68: name = "DHCP-Client"; break; case 69: name = "TFTP"; break; } sprintf(buf, "%u(%s)", port, name); return buf; } static void dump_eth_packet(const char *title, u8 *data, int len) { struct iphdr *ih = NULL; struct udphdr *uh = NULL; u16 protocol = 0; u8 ip_protocol = 0; u16 port = 0; protocol = (data[12]<<8) | data[13]; ih = (struct iphdr *) (data+ETH_HLEN); if (protocol == ETH_P_IP) { uh = (struct udphdr *) ((char *)ih + sizeof(struct iphdr)); ip_protocol = ih->protocol; port = ntohs(uh->dest); } else if (protocol == ETH_P_IPV6) { struct ipv6hdr *i6h = (struct ipv6hdr *) data; uh = (struct udphdr *) ((char *)i6h + sizeof(struct ipv6hdr)); ip_protocol = i6h->nexthdr; port = ntohs(uh->dest); } printk(KERN_DEBUG "[%s] len=%d, %s, %s, %s\n", title, len, get_protocol_name(protocol), get_ip_protocol_name(ip_protocol), get_port_name(port)); if (!(data[0] == 0xff && data[1] == 0xff)) { if (protocol == ETH_P_IP) { printk(KERN_DEBUG " src=%pI4\n", &ih->saddr); } else if (protocol == ETH_P_IPV6) { printk(KERN_DEBUG " src=%pI6\n", &ih->saddr); } } #if (DUMP_PACKET & DUMP_SDU_ALL) printk_hex(data, len); #else #if (DUMP_PACKET & DUMP_SDU_ARP) if (protocol == ETH_P_ARP) printk_hex(data, len); #endif #if (DUMP_PACKET & DUMP_SDU_IP) if (protocol == ETH_P_IP || protocol == ETH_P_IPV6) printk_hex(data, len); #else #if (DUMP_PACKET & DUMP_SDU_IP_TCP) if (ip_protocol == IPPROTO_TCP) printk_hex(data, len); #endif #if (DUMP_PACKET & DUMP_SDU_IP_UDP) if (ip_protocol == IPPROTO_UDP) printk_hex(data, len); #endif #if (DUMP_PACKET & DUMP_SDU_IP_ICMP) if (ip_protocol == IPPROTO_ICMP) printk_hex(data, len); #endif #endif #endif } #endif static inline int gdm_wimax_header(struct sk_buff **pskb) { u16 buf[HCI_HEADER_SIZE / sizeof(u16)]; struct sk_buff *skb = *pskb; int ret = 0; if (unlikely(skb_headroom(skb) < HCI_HEADER_SIZE)) { struct sk_buff *skb2; skb2 = skb_realloc_headroom(skb, HCI_HEADER_SIZE); if (skb2 == NULL) return -ENOMEM; if (skb->sk) skb_set_owner_w(skb2, skb->sk); kfree_skb(skb); skb = skb2; } skb_push(skb, HCI_HEADER_SIZE); buf[0] = H2B(WIMAX_TX_SDU); buf[1] = H2B(skb->len - HCI_HEADER_SIZE); memcpy(skb->data, buf, HCI_HEADER_SIZE); *pskb = skb; return ret; } static void gdm_wimax_event_rcv(struct net_device *dev, u16 type, void *msg, int len) { struct nic *nic = netdev_priv(dev); #if defined(DEBUG_HCI) u8 *buf = (u8 *) msg; u16 hci_cmd = (buf[0]<<8) | buf[1]; u16 hci_len = (buf[2]<<8) | buf[3]; printk(KERN_DEBUG "H=>D: 0x%04x(%d)\n", hci_cmd, hci_len); #endif gdm_wimax_send(nic, msg, len); } 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); free_event_entry(e); } list_for_each_entry_safe(e, temp, &wm_event.freeq, list) { list_del(&e->list); free_event_entry(e); } spin_unlock_irqrestore(&wm_event.evt_lock, flags); netlink_exit(wm_event.sock); wm_event.sock = NULL; } } static inline struct evt_entry *alloc_event_entry(void) { return kmalloc(sizeof(struct evt_entry), GFP_ATOMIC); } static inline void free_event_entry(struct evt_entry *e) { kfree(e); } 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 put_event_entry(struct evt_entry *e) { BUG_ON(!e); list_add_tail(&e->list, &wm_event.freeq); } static void __gdm_wimax_event_send(struct work_struct *work) { int idx; unsigned long flags; struct evt_entry *e; spin_lock_irqsave(&wm_event.evt_lock, flags); while (!list_empty(&wm_event.evtq)) { e = list_entry(wm_event.evtq.next, struct evt_entry, list); spin_unlock_irqrestore(&wm_event.evt_lock, flags); sscanf(e->dev->name, "wm%d", &idx); netlink_send(wm_event.sock, idx, 0, e->evt_data, e->size); spin_lock_irqsave(&wm_event.evt_lock, flags); list_del(&e->list); put_event_entry(e); } spin_unlock_irqrestore(&wm_event.evt_lock, flags); } static int gdm_wimax_event_send(struct net_device *dev, char *buf, int size) { struct evt_entry *e; unsigned long flags; #if defined(DEBUG_HCI) u16 hci_cmd = ((u8)buf[0]<<8) | (u8)buf[1]; u16 hci_len = ((u8)buf[2]<<8) | (u8)buf[3]; printk(KERN_DEBUG "D=>H: 0x%04x(%d)\n", hci_cmd, hci_len); #endif 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; } nic->stats.tx_packets++; nic->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; struct nic *nic = netdev_priv(dev); struct fsm_s *fsm = (struct fsm_s *) nic->sdk_data[SIOC_DATA_FSM].buf; #if defined(DEBUG_SDU) dump_eth_packet("TX", skb->data, skb->len); #endif ret = gdm_wimax_header(&skb); if (ret < 0) { skb_pull(skb, HCI_HEADER_SIZE); return ret; } #if !defined(LOOPBACK_TEST) if (!fsm) netdev_err(dev, "ASSERTION ERROR: fsm is NULL!!\n"); else if (fsm->m_status != M_CONNECTED) { netdev_emerg(dev, "ASSERTION ERROR: Device is NOT ready. status=%d\n", fsm->m_status); kfree_skb(skb); return 0; } #endif #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)]; u8 *pkt = (u8 *) &hci_pkt_buf[0]; 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_pkt_buf[0] = H2B(WIMAX_SET_INFO); /* cmd_evt */ hci_pkt_buf[1] = H2B(8); /* size */ pkt[4] = 0; /* T */ pkt[5] = 6; /* L */ memcpy(pkt + 6, mac_addr, dev->addr_len); /* V */ gdm_wimax_send(nic, pkt, 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 struct net_device_stats *gdm_wimax_stats(struct net_device *dev) { struct nic *nic = netdev_priv(dev); return &nic->stats; } static int gdm_wimax_open(struct net_device *dev) { struct nic *nic = netdev_priv(dev); struct fsm_s *fsm = (struct fsm_s *) 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 = (struct fsm_s *) 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 data_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 data_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 == NULL) 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_update_fsm(struct net_device *dev, struct fsm_s *new_fsm) { struct nic *nic = netdev_priv(dev); struct fsm_s *cur_fsm = (struct fsm_s *) 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; 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*/ gdm_update_fsm(dev, (struct fsm_s *) req->data.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; #define BIT_MULTI_CS 0 #define BIT_WIMAX 1 #define BIT_QOS 2 #define BIT_AGGREGATION 3 /* GetInformation mac address */ len = 0; hci->cmd_evt = H2B(WIMAX_GET_INFO); hci->data[len++] = TLV_T(T_MAC_ADDRESS); hci->length = H2B(len); gdm_wimax_send(nic, hci, HCI_HEADER_SIZE+len); val = (1<<BIT_WIMAX) | (1<<BIT_MULTI_CS); #if defined(CONFIG_WIMAX_GDM72XX_QOS) val |= (1<<BIT_QOS); #endif #if defined(CONFIG_WIMAX_GDM72XX_WIMAX2) val |= (1<<BIT_AGGREGATION); #endif /* Set capability */ len = 0; hci->cmd_evt = H2B(WIMAX_SET_INFO); hci->data[len++] = TLV_T(T_CAPABILITY); hci->data[len++] = TLV_L(T_CAPABILITY); val = DH2B(val); memcpy(&hci->data[len], &val, TLV_L(T_CAPABILITY)); len += TLV_L(T_CAPABILITY); hci->length = H2B(len); gdm_wimax_send(nic, hci, HCI_HEADER_SIZE+len); netdev_info(dev, "GDM WiMax Set CAPABILITY: 0x%08X\n", DB2H(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 = B2H(__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 = B2H(*(u16 *)&buf[0]); cmd_len = B2H(*(u16 *)&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 (T == TLV_T(T_MAC_ADDRESS)) { if (L != dev->addr_len) { netdev_err(dev, "%s Invalid inofrmation 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 nic *nic = netdev_priv(dev); struct sk_buff *skb; int ret; #if defined(DEBUG_SDU) dump_eth_packet("RX", buf, len); #endif skb = dev_alloc_skb(len + 2); if (!skb) { netdev_err(dev, "%s: dev_alloc_skb failed!\n", __func__); return; } skb_reserve(skb, 2); nic->stats.rx_packets++; nic->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 (B2H(hci->cmd_evt) != WIMAX_RX_SDU) { netdev_err(dev, "Wrong cmd_evt(0x%04X)\n", B2H(hci->cmd_evt)); break; } length = B2H(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 = B2H(*(u16 *)&buf[0]); cmd_len = B2H(*(u16 *)&buf[2]); if (len < cmd_len + HCI_HEADER_SIZE) { if (len) 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 gdm_wimax_ind_fsm_update(struct net_device *dev, struct fsm_s *fsm) { u16 buf[32 / sizeof(u16)]; u8 *hci_pkt_buf = (u8 *)&buf[0]; /* Indicate updating fsm */ buf[0] = H2B(WIMAX_FSM_UPDATE); buf[1] = H2B(sizeof(struct fsm_s)); memcpy(&hci_pkt_buf[HCI_HEADER_SIZE], fsm, sizeof(struct fsm_s)); gdm_wimax_event_send(dev, hci_pkt_buf, HCI_HEADER_SIZE + sizeof(struct fsm_s)); } 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 = H2B(WIMAX_IF_UPDOWN); hci->length = H2B(sizeof(up_down)); hci->data[0] = up_down; gdm_wimax_event_send(dev, (char *)hci, HCI_HEADER_SIZE+sizeof(up_down)); } static void rx_complete(void *arg, void *data, int 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, int 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); #if 0 /* Re-prepare WiMax device */ gdm_wimax_prepare_device(nic->netdev); #endif } } 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_get_stats = gdm_wimax_stats, .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 = (struct net_device *)alloc_netdev(sizeof(*nic), "wm%d", ether_setup); if (dev == NULL) { 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); memset(nic, 0, sizeof(*nic)); 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; #if defined(LOOPBACK_TEST) netif_start_queue(dev); netif_carrier_on(dev); #else netif_carrier_off(dev); #endif #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 = (struct fsm_s *) 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); }